FortiADC Configuration Management with Ansible

Deploy a Server Load Balancer (SLB) with Ansible

This blog post demonstrates how a Server Load Balancer (SLB) configuration infront of the EmployeeDB Applikation can be automaticly deployed with Ansible. This functionally is typically used by Application and DevOps Teams as part of a Continuous Integration / Continuous Deliver (CI/CD) Pipeline to automatically deploy an Application into Dev, Test or Production stage.

The Picture above shows the demo setup and the involved systems in a overview.

Demo Setup

The Picture below show the demo setup in detail with all involed systems and IP Addresses .

Verify the EmployeeDB Application deployment

The EmpliyeeDB Application bas in preparation for this demo already been deployed on the Backend Servers employeedb1, employeedb2 and employeedb3 along with the PostgreSQL database empliyeedb-sql. Let’s verify that they are up and running.

Screenshot

Ay you can see in the picture above, the three deployments can be reached by their IP Adress individually on HTTP port 8080. Be aware that the backend, in this configuration is not TLS/SSL encrypted as we are doing TLS/SSL offloading on the FortiADC to safe resources on the backend server.

Create an Ansible Variable File (fortiadc-lb-vars-employeedb.yaml)

We create first an Ansible values file: fortiadc-lb-vars-employeedb.yaml with all the definitions required for the ansible playbook to execute. With this, we have all the required configuration in one place independant from the playbook. This alowes us to use the same playbook again with the configuration for another application.

# Real Server Pool
pool_name: employeedb

# Virtual Server Configuration
virtual_server_name: ws-employeedb-fad-vs
virtual_server_ip: 10.2.1.115
virtual_server_interface: port1
virtual_server_port: 443
virtual_server_type: http
iptype: ipv4
vdom: root

# Certificate Configuration
ssl_cert: /home/fortinet/cert/fortidemo/k3s-apps-external.crt
ssl_key: /home/fortinet/cert/fortidemo/k3s-apps-external.key
local_cert_group: EMPLOYEEEDB_CERT_GROUP
client_ssl_profile: LB_CLIENT_SSL_EMPLOYEEEDB

# Real Server Pool Members
real_servers:
  - name: rs_employeedb1
    id: 1
    port: 8080
    status: enable
    ip: 10.1.1.211
    weight: 1

  - name: rs_employeedb2
    id: 2
    port: 8080
    status: enable
    ip: 10.1.1.212
    weight: 1

  - name: rs_employeedb3
    id: 3
    port: 8080
    status: enable
    ip: 10.1.1.213
    weight: 1

Protect Secrets with Ansible Vault (ansible-vault)

Its best practices to have all ansible configuration files clean without critical information such as user credentials, certificate and certificate keys as ansible configuration files typically are in stored in a version control system such as GIT where every one is able to see them. Therefor we are using ansible-vault to protect secrets in an encrypted file. Create a protected ansible-vault password in your home directory

=> echo "F0rt!net" > /home/fortinet/.ansible/vault_password
=> chmod 600 /home/fortinet/.ansible/vault_password
=> ls -la $HOME/.ansible/vault_password
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  -rw------- 1 fortinet fortinet 9 Nov 29 20:20 /home/fortinet/.ansible/vault_password
  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Create a vault file containing the ‘fortiadc_password’ password in clear text

=> cat /tmp/vault.yaml
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   # vault.yaml
   fortiadc_password: "F0rt!net"
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Encrypt the vault.yaml with ansible-vault

=> ansible-vault encrypt /tmp/vault.yaml --vault-password-file $HOME/.ansible/vault_password
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Encryption successful
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
=> cat /tmp/vault.yaml
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   $ANSIBLE_VAULT;1.1;AES256
   37376462356433376434373039636264636133353863313531626533656266643565633030653032
   3636323732383531323631316338666538633061333361370a393561303662386439353335376532
   34636535356631386634386539623032303431353837396234346438346662353333663537636564
   6631373734643865370a396631376539663965353165333863653735396431623838343061633537
   30383463373436663737343134303464646137313063663039336164396333356664393234363464
   6637313164326339333431303838633566346364343133303931
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Create an Ansible Inventory File

An Ansible inventory file (./inventory) is the file where you define which hosts (devices/servers) Ansible should manage and how to reach them. It’s essentially the target list for the automation.In our case it contains only our FortiADC, but it’s possible to add the FortiGate as well to deploy Firewal Rules releated to the EmployeDB application or to add the PostGRE SQL Server to deploy a database if non is there yet.

=> cat /tmp/inventory
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   [fortiadcs]
   fortiadc ansible_host=10.2.1.3 ansible_user="admin" ansible_password="{{ fortiadc_password }}"
              
   [fortiadcs:vars]
   ansible_network_os=fortinet.fortiadc.fadcos
   ansible_httpapi_use_ssl=yes
   ansible_httpapi_validate_certs=no
   ansible_httpapi_port=443
   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Create and Execute the Ansible Playbook

Let’s create the Ansible Playbook to upload the SSL/TSL Certificate, configure the Real Servers and Real Servers Pool, Health Check, Client SSL Profile and the Virtual Server. The order of these tasks are important, as some are depending on previous executed tasks.

=> cat ./fortiadc-lb-config-ssl.yaml
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

     - name: Manage FortiADC real servers
       hosts: fortiadcs
       collections:
         - fortinet.fortiadc
       connection: httpapi
       gather_facts: false
       vars_files:
         - /tmp/vault.yaml  # Load encrypted variables
       tasks:
         - name: Create/Verify real server and change status to 'disable'
           fadcos_real_server:
             action: add
             name: {{ item.name }}
             ip: {{ item.ip }}
             status: disable
             vdom: root
           loop: {{ real_servers }}
              
         - name: Update IP addresses of real servers
           fadcos_real_server:
             action: edit
             name: {{ item.name }}
             ip: {{ item.ip }}
             status: {{ item.status }}
             vdom: root
           loop: {{ real_servers }}
              
         - name: Manage Health Checks LBHC_HTTP_200
           fadcos_health_check:
             action: add
             name: LBHC_HTTP_200
             status_code: 200
             send_string: /
             dest_addr_type: ipv4
             hc_type: http
           no_log: true
              
         - name: Manage Real Server Pool $x{{ pool_name }}
           fadcos_real_server_pool:
             action: add
             name: {{ pool_name }}
             iptype: {{ iptype }}
             vdom: {{ vdom }}
             healthcheck: enable
             health_check_list:
               - LBHC_HTTP_200
              
         - name: Manage real server pool member
           fadcos_real_server_pool_member:
             action: add
             pool_name: {{ pool_name }}
             status: {{ item.status }}
             member_id: {{ item.id }}
             port: {{ item.port }}
             rs: {{ item.name }}
             weight: {{ item.weight }}
           loop: {{ real_servers }}
              
         - name: add CertKey
           fadcos_system_certificate_local_upload:
             action: add
             type: CertKey
             name: employeedb-ssl
             certificate_file: {{ ssl_cert }}
             key_file: {{ ssl_key }}
              
         - name: Manage Local Certificate Group
           fadcos_local_cert_group:
             action: add_group
             name: {{ local_cert_group }}
              
         - name: Manage Local Certificate Group Members
           fadcos_local_cert_group:
             action: add_member
             local_cert: employeedb-ssl
             name: {{ local_cert_group }}
              
         - name: Manage Client SSL Profile {{ client_ssl_profile }}
           fadcos_client_ssl_profile:
             action: add
             name: {{ client_ssl_profile }}
             backend_customized_ssl_ciphers_flag: enable
             backend_ssl_OCSP_stapling_support: disable
             backend_ssl_allowed_versions: sslv3 tlsv1.0 tlsv1.1 tlsv1.2
             backend_ssl_sni_forward: disable
             client_certificate_verify_mode: required
             client_sni_required: disable
             customized_ssl_ciphers_flag: disable
             forward_proxy: disable
             forward_proxy_local_signing_ca: SSLPROXY_LOCAL_CA
             http_forward_client_certificate: disable
             http_forward_client_certificate_header: X-Client-Cert
             local_certificate_group: {{ local_cert_group }}
             reject_ocsp_stapling_with_missing_nextupdate: disable
             ssl_allowed_versions: tlsv1.1 tlsv1.2
             ssl_dh_param_size: 1024bit
             ssl_dynamic_record_sizing: disable
             ssl_renegotiate_period: 0
             ssl_renegotiate_size: 0
             ssl_renegotiation: disable
             ssl_renegotiation_interval: -1
             ssl_secure_renegotiation: require
             ssl_session_cache_flag: enable
             use_tls_tickets: enable

         - name: Create basic virtual server {{ virtual_server_name }}
           fadcos_virtual_server:
             name: {{ virtual_server_name }}
             status: enable
             action: add
             iptype: ipv4
             ip: {{ virtual_server_ip }}
             pool: {{ pool_name }}
             port: {{ virtual_server_port }}
             interface: {{ virtual_server_interface }}
             profile: LB_PROF_HTTPS
             vstype: l7-load-balance
             client_ssl_profile: {{ client_ssl_profile }}
     ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Run the Ansible Playbook (./fortiadc-lb-config-ssl.yaml)

=>   ansible-playbook ./fortiadc-lb-config-ssl.yaml \
          -i ./inventory --extra-vars "@fortiadc-lb-vars-employeedb.yaml" \
          --vault-password-file /home/fortinet/.ansible/vault_password
     ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Using /etc/ansible/ansible.cfg as config file
     
     PLAY [Manage FortiADC real servers] ********************************************
     
     TASK [Create/Verify real server and change status to 'disable'] ****************
     changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.211', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 1, "ip": "10.1.1.211", "name": "rs_employeedb1", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.212', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 2, "ip": "10.1.1.212", "name": "rs_employeedb2", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.213', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 3, "ip": "10.1.1.213", "name": "rs_employeedb3", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     
     TASK [Update IP addresses of real servers] *************************************
     changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.211', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 1, "ip": "10.1.1.211", "name": "rs_employeedb1", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.212', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 2, "ip": "10.1.1.212", "name": "rs_employeedb2", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.213', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 3, "ip": "10.1.1.213", "name": "rs_employeedb3", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     
     TASK [Manage Health Checks LBHC_HTTP_200] **************************************
     changed:  => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true}
     
     TASK [Manage Real Server Pool $xemployeedb] ************************************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     TASK [Manage real server pool member] ******************************************
     changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.211', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 1, "ip": "10.1.1.211", "name": "rs_employeedb1", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.212', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 2, "ip": "10.1.1.212", "name": "rs_employeedb2", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.213', 'weight': 1}) => {"ansible_loop_var": "item", "changed": true, "item": {"id": 3, "ip": "10.1.1.213", "name": "rs_employeedb3", "port": 8080, "status": "enable", "weight": 1}, "res": {"payload": 0}}
     
     TASK  *************************************************************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     TASK [Manage Local Certificate Group] ******************************************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     TASK [Manage Local Certificate Group Members] **********************************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     TASK [Manage Client SSL Profile LB_CLIENT_SSL_EMPLOYEEDB] **********************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     TASK [Create basic virtual server ws-employeedb-fad-vs] ************************
     changed:  => {"changed": true, "res": {"payload": 0}}
     
     PLAY RECAP *********************************************************************
     fortiadc                   : ok=10   changed=10   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Verify the EmployeeDB application deployment

After the Ansible Playbook has been successfully completed, we can now verify that the application EmployeeDB is working correctly. The following command connection to the Java Spring Boot Actuator interface to verify the application’s health.

=> curl https://employeedb.apps.fortidemo.net/actuator/health 2>/dev/null | jq -r 
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   {
   . "status": "UP"=fortinet.fortiadc.fadcos
   }              
   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Now let’s see the EmployeeDB Application shown from the Web Browser.

As we can see in the Picture above, the application Link https://empoyedb.apps.fortidemo.net opens with a valid certificate loadbalanced on the three backend servers.

Verify FortiADC Configuration

In the FortiADC UI we can clearly see that the Cirtual Server ws-empliyeedb-fad-vs has been created with the IP Address: 10.2.1.115

Screenshot

The detail configuration shows that the Ansible Playbook created a Layer-7 Load Balancer.

The information in the ‘General’ tab shows the Load Balancer Profile: HB_PROF_HTTPS containing our Health Check configuration, the Client SSL Profile containing the SSL/TLS Certificate Group with the Certificate and it shows the new created Real Server Pool: employeedb.

Screenshot – Client SSL Profiles
Screenshot – Client SSL Profile: LB_CLIENT_SSL_EMPLOYEEDB details

The Client SSL Profile: LB_CLIENT_SSL_EMPLOYEEDB defines supported SSL Cipher and supported SSL Versions, as well as the Certificate Group: EMPLOYEEDB_CERT_GROUP containing the SSL/TLS certificate shown below.

Screenshot – Local Certificate Group

TIn the picture below, you can see the definition of the Real Server Pool: employeedb just created with the three Real Servers employeedb1, employeedb2 and employeedb3 that act as the EmployeeDB Application backend servers.

Screenshot – FortiADC Real Server Pool: empliyeedb

Test Load Balancing behavior

In the following secrion we would like to test the Load Balancing behavior. Currently we have selected Round Robin as the Load Balancing method. Let’s generate again some traffic and watch the balancing across the Real Servers. To create the load, we use the following script.

#!/bin/bash
# ============================================================================================
# File: ........: genTrafficDocker.sh
# Demo Package .: fortiadc-slb-employdb-ansible
# Language .....: bash
# Author .......: Sacha Dubois/span>
# --------------------------------------------------------------------------------------------
# Category .....: Ansible
# Description ..: Generates load on the actuator/health endpoint and count entries in the logs
# ============================================================================================

st1=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null fortinet@10.1.1.211 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")
st2=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null fortinet@10.1.1.212 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")
st3=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null fortinet@10.1.1.213 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")

ls1=0; ls2=0; ls3=0; cnt=1
while [ $cnt -le 10 ]; do
  curl https://${APPNAME}.${DOMAIN}/actuator/health > /dev/null 2>&1

  cn1=$(ssh fortinet@10.1.1.211 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")
  cn2=$(ssh fortinet@10.1.1.212 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")
  cn3=$(ssh fortinet@10.1.1.213 -n "docker logs edb" 2>/dev/null | grep -c "GET \"/actuator")

  let tt1=cn1-st1
  let tt2=cn2-st2
  let tt3=cn3-st3

  tx1=$(printf "%02d\n" $tt1)
  tx2=$(printf "%02d\n" $tt2)
  tx3=$(printf "%02d\n" $tt3)
  hdr=$(printf "%03d\n" $cnt)

  echo -e "     [${hdr}] https://${APPNAME}.${DOMAIN}/actuator/health                       $tx1   $tx2   $tx3"

  ls1=$tt1; ls2=$tt2; ls3=$tt3
  let cnt=cnt+1
  sleep 2
done
=>  ./genTrafficDocker.sh
    -----------------------------------------------------------------------------------------------------------------
    [001] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [00]   10.1.1.213 [00]
    [002] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [01]   10.1.1.213 [00]
    [003] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [01]   10.1.1.213 [01]
    [004] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [02]   10.1.1.212 [01]   10.1.1.213 [01]
    [005] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [02]   10.1.1.212 [02]   10.1.1.213 [01]
    [006] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [02]   10.1.1.212 [02]   10.1.1.213 [02]
    [007] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [03]   10.1.1.212 [02]   10.1.1.213 [02]
    [008] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [03]   10.1.1.212 [03]   10.1.1.213 [02]
    [009] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [03]   10.1.1.212 [03]   10.1.1.213 [03]
    [010] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [04]   10.1.1.212 [03]   10.1.1.213 [03]
    -----------------------------------------------------------------------------------------------------------------

The test shows that the packages are evenly distributed over the three Real Servers. Now we are going to modify the configuration that each Member gets the following traffic weighing (Member-1: 1, Member-2, 3 and Member-3: 5).

=> cat ./fortiadc-lb-vars-employeedb.yaml 
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  # Real Server Pool
  pool_name: employeedb
              
  # Virtual Server Configuration
  virtual_server_name: employeedb
  virtual_server_ip: 10.2.2.50
  virtual_server_interface: port3
  virtual_server_port: 80
  virtual_server_type: http
  iptype: ipv4
  vdom: root

  # Real Server Pool Members
  real_servers:
    - name: rs_employeedb1
      id: 1
      port: 8080
      status: enable
      ip: 10.1.1.211
      weight: 1

    - name: rs_employeedb2
      id: 2
      port: 8080
      status: enable
      ip: 10.1.1.212
      weight: 3

    - name: rs_employeedb3
      id: 3
      port: 8080
      status: enable
      ip: 10.1.1.213
      weight: 5            
   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

To only update the Real Server Pool Members, we create an Real Server Pool Member Ansible Playbook

=> cat ./fortiadc-lb-vars-employeedb.yaml 
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   - name: Manage FortiADC real servers
  hosts: fortiadcs
  collections:
    - fortinet.fortiadc
  connection: httpapi
  gather_facts: false
  vars_files:
    - /tmp/vault.yaml  # Load encrypted variables
              
  tasks:
    - name: Manage real server pool member
      fadcos_real_server_pool_member:
        action: edit
        pool_name: "{{ pool_name }}"
        status: "{{ item.status }}"
        member_id: "{{ item.id }}"
        port: "{{ item.port }}"
        rs: "{{ item.name }}"
        weight: "{{ item.weight }}"
      loop: "{{ real_servers }}"     
   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Configure the Server Load Balancer with the Ansible Playbook

=> ansible-playbook ./fortiadc-lb-member-update.yaml \
          -i /tmp/inventory --extra-vars "@./fortiadc-lb-vars-employeedb.yaml" \
          --vault-password-file /home/fortinet/.ansible/vault_password
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    PLAY [Manage FortiADC real servers] ********************************************
     
    TASK [Manage real server pool member] ******************************************
    changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.211', 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.212', 'weight': 3})
    changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.213', 'weight': 5})
     
    PLAY RECAP *********************************************************************
    fortiadc                   : ok=1    changed=1    unreachable=0    failed=0     skipped=0    rescued=0    ignored=0
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Let’s generate again some traffic and watch the balancing across the Real Servers with the modified weighing. As we can see in the output below, Member-1 is getting only one request where Member-3 is having the most of the traffic.

=>  ./genTrafficDocker.sh
    -----------------------------------------------------------------------------------------------------------------
    [001] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [00]   10.1.1.213 [01]
    [002] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [01]   10.1.1.213 [01]
    [003] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [01]   10.1.1.213 [02]
    [004] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [02]   10.1.1.213 [02]
    [005] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [02]   10.1.1.213 [03]
    [006] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [03]   10.1.1.213 [03]
    [007] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [03]   10.1.1.213 [04]
    [008] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [00]   10.1.1.212 [03]   10.1.1.213 [05]
    [009] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [03]   10.1.1.213 [05]
    [010] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [03]   10.1.1.213 [06]
    -----------------------------------------------------------------------------------------------------------------

We are going to modify the configuration that Member-2 gets disabled. This is useful in case that a Real Server needs to be taken down for maintenance and the packages should be distributed to the other Servers instead.

=> cat /tmp/fortiadc-lb-vars-employeedb.yaml
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   # Real Server Pool
    pool_name: employeedb
              
   # Virtual Server Configuration
   virtual_server_name: employeedb
   virtual_server_ip: 10.2.2.50
   virtual_server_interface: port3
   virtual_server_port: 80
   virtual_server_type: http
   iptype: ipv4
   vdom: root

   # Real Server Pool Members
   real_servers:
     - name: rs_employeedb1
       id: 1
       port: 8080
       status: enable
       ip: 10.1.1.211
       weight: 1

     - name: rs_employeedb2
       id: 2
       port: 8080
       status: disable
       ip: 10.1.1.212
       weight: 1

     - name: rs_employeedb3
       id: 3
       port: 8080
       status: enable
       ip: 10.1.1.213
       weight: 1
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

To only update the Real Server Pool Members, we create an Real Server Pool Member Ansible Playbook

=> cat ./fortiadc-lb-member-update.yaml
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   - name: Manage FortiADC real servers
     hosts: fortiadcs
     collections:
       - fortinet.fortiadc
     connection: httpapi
     gather_facts: false
     vars_files:
       - /tmp/vault.yaml  # Load encrypted variables
              
     tasks:
       - name: Manage real server pool member
         fadcos_real_server_pool_member:
           action: edit
           pool_name: "{{ pool_name }}"
           status: "{{ item.status }}"
           member_id: "{{ item.id }}"
           port: "{{ item.port }}"
           rs: "{{ item.name }}"
           weight: "{{ item.weight }}"
         loop: "{{ real_servers }}"
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Configure the Server Load Balancer with the Ansible Playbook

=> ansible-playbook ./fortiadc-lb-member-update.yaml \
          -i /tmp/inventory --extra-vars "@./fortiadc-lb-vars-employeedb.yaml" \
          --vault-password-file /home/fortinet/.ansible/vault_password
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    PLAY [Manage FortiADC real servers] ********************************************
     
    TASK [Manage real server pool member] ******************************************
    changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.211', 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'disable', 'ip': '10.1.1.212', 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': '10.1.1.213', 'weight': 1})
     
    PLAY RECAP *********************************************************************
    fortiadc                   : ok=1    changed=1    unreachable=0    failed=0     skipped=0    rescued=0    ignored=0
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Let’s generate again some traffic and watch the balancing.

=>  ./genTrafficDocker.sh
    -----------------------------------------------------------------------------------------------------------------
    [001] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [00]   10.1.1.213 [00]
    [002] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [01]   10.1.1.212 [00]   10.1.1.213 [01]
    [003] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [02]   10.1.1.212 [00]   10.1.1.213 [01]
    [004] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [02]   10.1.1.212 [00]   10.1.1.213 [02]
    [005] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [03]   10.1.1.212 [00]   10.1.1.213 [02]
    [006] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [03]   10.1.1.212 [00]   10.1.1.213 [03]
    [007] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [04]   10.1.1.212 [00]   10.1.1.213 [03]
    [008] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [04]   10.1.1.212 [00]   10.1.1.213 [04]
    [009] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [05]   10.1.1.212 [00]   10.1.1.213 [04]
    [010] https://employeedb.apps.fortidemo.net/actuator/health   10.1.1.211 [05]   10.1.1.212 [00]   10.1.1.213 [05]
    -----------------------------------------------------------------------------------------------------------------

Cleaning Up

Let’s review the ansible variable file: fortiadc-lb-vars-employeedb.yaml and the ./inventory created while deploying the EmpliyeeDB Application.

# Real Server Pool
pool_name: employeedb

# Virtual Server Configuration
virtual_server_name: ws-employeedb-fad-vs
virtual_server_ip: 10.2.1.115
virtual_server_interface: port1
virtual_server_port: 443
virtual_server_type: http
iptype: ipv4
vdom: root

# Certificate Configuration
ssl_cert: /home/fortinet/cert/fortidemo/k3s-apps-external.crt
ssl_key: /home/fortinet/cert/fortidemo/k3s-apps-external.key
local_cert_group: EMPLOYEEEDB_CERT_GROUP
client_ssl_profile: LB_CLIENT_SSL_EMPLOYEEEDB

# Real Server Pool Members
real_servers:
  - name: rs_employeedb1
    id: 1
    port: 8080
    status: enable
    ip: 10.1.1.211
    weight: 1

  - name: rs_employeedb2
    id: 2
    port: 8080
    status: enable
    ip: 10.1.1.212
    weight: 1

  - name: rs_employeedb3
    id: 3
    port: 8080
    status: enable
    ip: 10.1.1.213
    weight: 1
=> cat /tmp/inventory
   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   [fortiadcs]
   fortiadc ansible_host=10.2.1.3 ansible_user="admin" ansible_password="{{ fortiadc_password }}"
              
   [fortiadcs:vars]
   ansible_network_os=fortinet.fortiadc.fadcos
   ansible_httpapi_use_ssl=yes
   ansible_httpapi_validate_certs=no
   ansible_httpapi_port=443
   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

To delete the configuaration we need to create a removal Playbook to cleanup the configuration

=>  cat /tmp/fortiadc-lb-delete-ssl.yaml
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- name: Manage FortiADC real servers
  hosts: fortiadcs
  collections:
    - fortinet.fortiadc
  connection: httpapi
  gather_facts: false
  vars_files:
    - /tmp/vault.yaml  # Load encrypted variables
  tasks:
    - name: Delete virtual server {{ virtual_server_name }}
      fadcos_virtual_server:
        name: "{{ virtual_server_name }}"
        action: delete
        ip: "{{ virtual_server_ip }}"
        pool: "{{ pool_name }}"
        port: "{{ virtual_server_port }}"
        interface: "{{ virtual_server_interface }}"
        profile: LB_PROF_HTTPS
        vstype: l7-load-balance
        client_ssl_profile: "{{ client_ssl_profile }}"
          
    - name: Delete virtual server
      fadcos_virtual_server:
        action: delete
        name: "{{ virtual_server_name }}"
           
    - name: Delete real server pool member
      fadcos_real_server_pool_member:
        action: delete
        pool_name: "{{ pool_name }}"
        member_id: "{{ item.id }}"
        rs: "{{ item.name }}"
      loop: "{{ real_servers }}"
            
    - name: Delete the Real Server Pool
      fadcos_real_server_pool:
        action: delete
        name: "{{ pool_name }}"
             
    - name: Delete the real server
      fadcos_real_server:
        action: delete
        name: "{{ item.name }}"
      loop: "{{ real_servers }}"
             
    - name: Delete Health Checks
      fadcos_health_check: 
        action: delete 
        name: LBHC_HTTP_200
      no_log: true
                 
    - name: Manage Client SSL Profile {{ client_ssl_profile }}
      fadcos_client_ssl_profile:
        action: delete
        name: "{{ client_ssl_profile }}"
                    
    - name: Manage Local Certificate Group Members
      fadcos_local_cert_group:
        action: delete_member
        member_id: 1
        local_cert: employeedb-ssl
        name: "{{ local_cert_group }}"
              
    - name: Manage Local Certificate Group
      fadcos_local_cert_group:
        action: delete_group
        name: "{{ local_cert_group }}"
               
    - name: Delete Local Certificate
      fadcos_system_certificate_local:
        action: remove
        name: employeedb-ssl
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Delete the Server Load Balancer with the Ansible Playbook

 => ansible-playbook /tmp/fortiadc-lb-delete-ssl.yaml \
          -i /tmp/inventory --extra-vars "@/tmp/fortiadc-lb-vars-employeedb.yaml" \
          --vault-password-file /home/fortinet/.ansible/vault_password
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    PLAY [Manage FortiADC real servers] ********************************************
     
    TASK [Create basic virtual server ws-employeedb-fad-vs] ************************
    changed: 
     
    TASK [Delete virtual server] ***************************************************
    ok: 
     
    TASK [Delete real server pool member] ******************************************
    changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port':     8080, 'status': 'enable', 'ip': None, 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': None, 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port':     8080, 'status': 'enable', 'ip': None, 'weight': 1})
     
    TASK [Delete the Real Server Pool] *********************************************
    changed: 
     
    TASK [Delete the real server] **************************************************
    changed:  => (item={'name': 'rs_employeedb1', 'id': 1, 'port':     8080, 'status': 'enable', 'ip': None, 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb2', 'id': 2, 'port': 8080, 'status': 'enable', 'ip': None, 'weight': 1})
    changed:  => (item={'name': 'rs_employeedb3', 'id': 3, 'port': 8080, 'status': 'enable', 'ip': None, 'weight': 1})
     
    TASK [Delete Health Checks] ****************************************************
    changed: 
     
    TASK [Manage Client SSL Profile LB_CLIENT_SSL_EMPLOYEEDB] **********************
    changed: 
     
    TASK [Manage Local Certificate Group Members] **********************************
    changed: 
     
    TASK [Manage Local Certificate Group] ******************************************
    changed: 
     
    TASK [Delete Local Certificate] ************************************************
    changed: 
     PLAY RECAP *********************************************************************
    fortiadc                   : ok=10   changed=9    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Leave a Comment