[python]creating pool without F5 sdk

This is a sample code to create a pool, I will add on option to add members on later post.

This code is a function of the greater project i am planning as a personal hobby.

def create_bigip_pool():
    lbmethod_option = {
        "1": "round-robin",
        "2": "least-connections-member",
        "3": "least-connections-node",
        "4": "least-sessions"
    }
    pool_name = input("Enter pool name: ")
    if pool_name in list_bigip_pools():
        print("{} is already exist in bigip!".format(pool_name))
        logging.error("{} is already exist in bigip!".format(pool_name))
        exit(1)
    else:
        print("Choose load balancing method\n")
        for lbmethod in lbmethod_option:
            print(lbmethod, lbmethod_option[lbmethod])
        choice = input("Your choice: ")
        if not choice:
            lbmode = "round-robin"
        elif choice is "1" or choice <= "4":
            lbmode = lbmethod_option[choice]
        else:
            lbmode = "round-robin"
            print("You have entered invalid choice, round robin is chosen.")
    body = {
        "kind": "tm:ltm:pool:poolstate",
        "name": pool_name,
        "loadBalancingMode": lbmode
    }
    for bigip_address in BIGIP_ADDRESSES:
        try:
            bigip.post("https://" + bigip_address + LTM_BASE_POOL_URI, json.dumps(body))
        except Exception as e:
            logging.error(e)
            exit(1)

This is the result when testing the function:

Enter pool name: pool-MarvelHeroes
Choose load balancing method

1 round-robin
2 least-connections-member
3 least-connections-node
4 least-sessions
Your choice: 4

Process finished with exit code 0

And a fail test:

Enter pool name: pool-MarvelHeroes
pool-MarvelHeroes is already exist in bigip!

Process finished with exit code 1

Advertisements
Posted in Python, Scripting | Tagged , , , , | Leave a comment

[powershell]Creating access request on Tufin using REST API

This script uses a user filled in excel sheet (xlsx), the sheet contains 3 columns, source, destination and service. Since Tufin SecureChange cannot do remove or deny, asking user to choose remove or deny is meaningless.

the below script has tested working, but a lot of exception handling and error handling has not been in place yet…so this is actually more of a demo and not a final product. If the script reads an empty excel sheet then Tufin SecureChange will throw a http 400 bad request kind of thing.

The concept is to break the entire ticket xml template into blocks, the blocks are broken into source blocks, destination blocks, service blocks and finally access request blocks, these four blocks are dynamic, and they need to be treated like lego sets, depending on how many values the blocks will increment with values specified in the excel sheet.

Algorithm
1. Create an excel.application com-object, open the workbook, then the worksheet.
2. Store the name of the worksheets into a variable. The data type store in this variable is actually a group of hash tables.
3. From the first worksheet, check if the second row of the three columns (1st row is column name) is empty, if it is just quit.
4. Collect the first column, then the next until the service column.
5. Use the collections and put them into xml templates, then concatenate them to the access request template.
6. if there is more than one worksheet, all source, destination and service collections will be re-initialize, and the cycle from 4 to 5 repeats itself until there is no more valid worksheet left.

#ignore certificate validation
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
#force to use TLSv1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12


function GatherSrcHosts($new_host){
$src_addr = @" 
<source type="IP">
    <ip_address>$new_host</ip_address>
    <netmask>255.255.255.255</netmask>
</source>
"@
return $src_addr + "`r"
}

function GatherDstHosts($new_host) {
$dst_addr = @"
<destination type="IP">
    <ip_address>$new_host</ip_address>
    <netmask>255.255.255.255</netmask>
</destination>
"@
return $dst_addr + "`r"
}

function GatherServices($service) {
if($service.ToString().ToLower() -like "*tcp*")
{
    $proto = "TCP"
    $port_num = $service.Replace("tcp","")
}
elseif($service.ToString().ToLower() -like "*udp*") {
    $proto = "UDP"
    $port_num = $service.Replace("udp","")
}
else {
    $proto = "TCP"
    $port_num = $service.Replace("tcp","")
}

$svc = @"
<service type="PROTOCOL">
    <protocol>$proto</protocol>
    <port>$port_num</port>
</service>   
"@
return $svc + "`r"
}

function GetAccessRequests($src_collections,$dst_collections,$service)
{
    $accessRequest = @"
<access_request>
   <users>
     <user>Any</user>
   </users>
     <sources>
        $src_collections
     </sources>
     <destinations>
        $dst_collections                    
     </destinations>
     <services>
        $services
     </services>
     <action>Accept</action>
     <labels/>
</access_request>
"@
return $accessRequest + "`r"
}

$tufinWorkflow = @"
<subject>Test</subject>
<priority>Normal</priority>
<domain_name></domain_name>
<workflow>
    <name>Firewall_Automation</name>
    <uses_topology>true</uses_topology>
</workflow>
"@

function Obfuscate_Password($username){
    if ((Test-Path .\fwr_s.do) -eq $true) {
    $password = Get-Content ".\fwr_s.do" | ConvertTo-SecureString
    $credential = New-Object System.Management.Automation.PSCredential($username,$password)
    } else {
        Read-Host $username -AsSecureString | ConvertFrom-SecureString | Out-File -NoClobber .\fwr_s.do
        #This is to allow time for the file to be written, otherwise there is a possibility that invoke-restmethod will throw an exception that is really weird.
        #Eg. Invoke-RestMethod : You must write ContentLength bytes to the request stream before calling [Begin]GetResponse.
        Start-Sleep -Seconds 2
        $password = Get-Content ".\fwr_s.do" | ConvertTo-SecureString
        $credential = New-Object System.Management.Automation.PSCredential($username,$password)
        }
    return $credential.GetNetworkCredential().Password
}

#1 is the title, for user to see
$a = 2 #source
$b = 2 #destination
$c = 2 #service
$d = 2 #action
$e = 2 #comment
$src = @() # source ip address collection
$dst = @()
$srcHosts = @() # source hostnames collection
$dstHosts = @()
$services = @() # collection of services
$src_collections = @()
$dst_collections = @()
$accessRequest = @()
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false # To prevent the excel from opening visibly.
$wb = $xl.Workbooks.Open("H:\Tufin\tests\Rulebook.xlsx")


#uncomment to check the sheet in a workbook.
#$wb.Sheets | Select-Object -Property Name

$rule_name = $wb.Worksheets | Select-Object "Name"
foreach($rule in $rule_name)
{
    $ws = $wb.Sheets.item($rule.Name)
    if($ws.Range("A" + $a).Text -eq "" -or $ws.Range("B" + $b).Text -eq "" -or $ws.Range("C" + $c).Text -eq "")
    {
        break
    }
        # can use as a function for this part
    while($true)
    {
        if($ws.Range("A" + $a).Text -ne "") # collect until an empty cell.
            {
                $srcHosts += $ws.Range("A" + $a).Text
        }
        else
        {
            break
        }
        $a += 1
    }
    foreach($srcHost in $srcHosts) 
    {
        $src += [System.Net.DNS]::GetHostEntry($srcHost).AddressList.IPAddressToString
    }

    #consider a function
    while($true)
    {
        if($ws.Range("B" + $b).Text -ne "")
        {
            $dstHosts += $ws.Range("B" + $b).Text
        }
        else
        {
            break
        }
        $b += 1
    }
    foreach($dstHost in $dstHosts)
    {
        $dst += [System.Net.DNS]::GetHostEntry($dstHost).AddressList.IPAddressToString
    }

    while($true)
    {
        if($ws.Range("C" + $c).Text -ne "")
        {
            $services_s += @($ws.Range("C" + $c).Text)
        }
        else
        {
            break
        }
        $c += 1
    }
        #$ws.Range("D2").Text
        if($ws.Range("E2").Text -ne "")
        {
            $comment = $ws.Range("E2").Text   
        }
    
    foreach($dst_ip in $dst) {
        $dst_collections += GatherDstHosts($dst_ip)
   
    }

    foreach($src_ip in $src) {
        $src_collections += GatherSrcHosts($src_ip)
    }

    foreach($service in $services_s)
    {
        $services += GatherServices($service)
    }
#join access request on each worksheet
    $accessRequest += @"
<access_request>
   <users>
     <user>Any</user>
   </users>
     <sources>
        $src_collections
     </sources>
     <destinations>
        $dst_collections                    
     </destinations>
     <services>
        $services
     </services>
     <action>Accept</action>
     <labels/>
</access_request>
#reinitialize the values, otherwise there will be duplicates.
"@
    $a = 2 #source
    $b = 2 #destination
    $c = 2 #service
    $d = 2 #action
    $e = 2 #comment
    $src = @() # source ip address collection
    $dst = @()
    $srcHosts = @() # source hostnames collection
    $dstHosts = @()
    $services_s = @()
    $services = @()
    $src_collections = @()
    $dst_collections = @()
    
}
    #Clean up
    $xl.Workbooks.Close()
    $xl.Quit()
    [void][System.Runtime.Interopservices.Marshal]::FinalReleaseComObject($ws)
    [void][System.Runtime.Interopservices.Marshal]::FinalReleaseComObject($wb)
    [void][System.Runtime.Interopservices.Marshal]::FinalReleaseComObject($xl)
    [GC]::Collect()


$body = @"
<ticket>
$tufinWorkflow
<steps>
      <step>
          <name>Open request</name>
      <tasks>
           <task>
             <fields>
                <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="multi_access_request">
                    <name>Firewall Rule</name>
$accessRequest
</field>
</fields>
</task>
</tasks>
</step>
</steps>
<comments/>
</ticket>
"@

$usr = "admin"
$pwd = Obfuscate_Password($usr)
$cred = "${usr}:${pwd}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($cred)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ Authorization = $basicAuthValue }
#sleep to defeat the z monster = "You must write ContentLength bytes to the request stream before calling [Begin]GetResponse."
Start-Sleep -Seconds 5 
try
{
  Invoke-RestMethod -Uri "https://$Tufin_Secure_Change_IP/securechangeworkflow/api/securechange/tickets/" -Method Post -ContentType "application/xml" -Headers $headers -Body $body
}
catch {
    $err=$_.Exception
    $err.Message
}
Posted in Powershell, Scripting | Tagged , , , , | Leave a comment

[python]Getting VS name and VS’ profile with iControl API

I have written two more functions to check the profile type of a virtual server, previously i tried to use F5 SDK unfortunately this SDK does not have attributes for fastL4 and fasthttp.

Below are the codes:

def bigip_vs_profile_type(vs_name):
    for bigip_address in BIGIP_ADDRESSES:
        try:
            response = bigip.get("https://" + bigip_address + LTM_BASE_VS_URI + PARTITION + vs_name + "/profiles")
            vs_configs = response.json()
            logging.info("Successfully connected to bigip, retrieving {} profile type...".format(vs_name))
        except Exception as e:
            logging.error(e)
            exit(1)
    for vs_config in vs_configs['items']:
        vs_profile = vs_config['name']
    return vs_profile


def list_bigip_vs():
    vs_s = []
    for bigip_address in BIGIP_ADDRESSES:
        try:
            response = bigip.get("https://" + bigip_address + LTM_BASE_VS_URI)
            vs_collections = response.json()
            logging.info("Successfully connected to bigip, collecting vs parameters...")
        except Exception as e:
            logging.error(e)
            exit(1)
    for vs in vs_collections['items']:
        vs_s.append(vs['name'])
        logging.info("Collecting available virtual servers' names...")
    return vs_s
Posted in Python, Scripting | Tagged , , | Leave a comment

Must read

This is a very in-depth guide on what hashtables can be used in powershell, must read. I have some situations which I am considering hashtables such as storing the source, destination and service to one row of firewall rule, and concatenate the next firewall rule with the same properties.

See this https://kevinmarquette.github.io/2016-11-06-powershell-hashtable-everything-you-wanted-to-know-about/

Posted in General stuffs | Leave a comment

[python]Listing items with iControl API without using F5 SDK

After I have discovered that F5 SDK could not do certain things, I have decided to learn how to call the REST APIs using request and parse the json response. The first step i am learning is to use GET after that I will use POST using the json.dumps method.

This code is part of my personal project for doing auto provisioning with user’s input. A real life scenario is that there is an orchestrator with a workflow for creating virtual machines, the workflow includes assigning vlan id and ip address and gateway, user puts a request that the newly created servers should be load balanced.

Anyway while building my interactive auto provisioning project here are the two functions for doing listing of pools and pool members, in real world situation a script should dynamically poll the existing available resources from F5 bigip lots of demo scripts i have come across gave impractical examples like hardcode the resources and collections.

I will be building a series of scripts and share in my series of post, if there is a better way of writing it please put in the comments.

Functions have to be tested before proceeding to the next functions, this is to ensure the entire development has manageable bugs.

import requests, logging

# This code from solution of Shazow reference:
# https://stackoverflow.com/questions/27981545/suppress-insecurerequestwarning-unverified-https-request-is-being-made-in-pytho
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# setting up logging
logging.basicConfig(filename="bigip_script.log",
                    format="%(asctime)s %(levelname)s:%(message)s",
                    level=logging.INFO)

# parameters of bigip for readability
USERNAME = "admin"
PASSWORD = "121278"
LTM_BASE_URI = '/mgmt/tm/ltm'
LTM_BASE_POOL_URI = LTM_BASE_URI + '/pool'
LTM_BASE_VS_URI = LTM_BASE_URI + '/virtual'
PARTITION = '/~Common~'
BIGIP_ADDRESSES = ["192.168.1.11"]

# setting up for the REST API request
bigip = requests.session()
bigip.auth = requests.auth.HTTPBasicAuth(USERNAME, PASSWORD)
bigip.headers.update({"Content-Type" : "application/json"})
bigip.verify = False


def list_bigip_pools():
    pools = []
    for bigip_address in BIGIP_ADDRESSES:
        try:
            response = bigip.get("https://" + bigip_address + LTM_BASE_POOL_URI)
            pool_collections = response.json()
            logging.info("Successfully connected to bigip, collecting the pool parameters...")
        except Exception as e:
            logging.error(e)
            exit(1)
    for pool in pool_collections['items']:
        pools.append(pool['name'])
        logging.info("Collecting avaialble pool names...")
    return pools


def list_bigip_pool_members(pool_name):
    pool_members = []
    for bigip_address in BIGIP_ADDRESSES:
        try:
            response = bigip.get("https://" + bigip_address + LTM_BASE_POOL_URI + PARTITION + pool_name + "/members")
            member_collections = response.json()
            logging.info("Collecting members parameter from pool {}".format(pool_name))
        except Exception as e:
            logging.error(e)
            exit(1)
    for member in member_collections['items']:
        pool_members.append(member['name'])
        logging.info("Collecting member names from pool {}".format(pool_name))
    return pool_members


if __name__ == '__main__':
# testing the operabability of the two functions.
    pools = list_bigip_pools()
    for pool in pools:
        members = list_bigip_pool_members(pool)
        print("Members of pool {}".format(pool))
        for member in members:
            print(member)

Here is how it looks like:

Members of pool google_dns
8.8.4.4:53
8.8.8.8:53
Members of pool pool-Gwen
Members of pool pool-Gwenpool
192.168.100.1:80
192.168.100.3:80
192.168.200.2:80
Members of pool pool-Luna

Here is the log:

2018-04-24 01:33:08,865 INFO:Successfully connected to bigip, collecting the pool parameters...
2018-04-24 01:33:08,869 INFO:Collecting avaialble pool names...
2018-04-24 01:33:08,869 INFO:Collecting avaialble pool names...
2018-04-24 01:33:08,869 INFO:Collecting avaialble pool names...
2018-04-24 01:33:08,869 INFO:Collecting avaialble pool names...
2018-04-24 01:33:08,891 INFO:Collecting members parameter from pool google_dns
2018-04-24 01:33:08,891 INFO:Collecting member names from pool google_dns
2018-04-24 01:33:08,891 INFO:Collecting member names from pool google_dns
2018-04-24 01:33:08,921 INFO:Collecting members parameter from pool pool-Gwen
2018-04-24 01:33:08,938 INFO:Collecting members parameter from pool pool-Gwenpool
2018-04-24 01:33:08,938 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:33:08,938 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:33:08,938 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:33:08,963 INFO:Collecting members parameter from pool pool-Luna
2018-04-24 01:41:28,065 INFO:Successfully connected to bigip, collecting the pool parameters...
2018-04-24 01:41:28,067 INFO:Collecting avaialble pool names...
2018-04-24 01:41:28,067 INFO:Collecting avaialble pool names...
2018-04-24 01:41:28,067 INFO:Collecting avaialble pool names...
2018-04-24 01:41:28,067 INFO:Collecting avaialble pool names...
2018-04-24 01:41:28,092 INFO:Collecting members parameter from pool google_dns
2018-04-24 01:41:28,092 INFO:Collecting member names from pool google_dns
2018-04-24 01:41:28,092 INFO:Collecting member names from pool google_dns
2018-04-24 01:41:28,113 INFO:Collecting members parameter from pool pool-Gwen
2018-04-24 01:41:28,133 INFO:Collecting members parameter from pool pool-Gwenpool
2018-04-24 01:41:28,133 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:41:28,133 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:41:28,133 INFO:Collecting member names from pool pool-Gwenpool
2018-04-24 01:41:28,157 INFO:Collecting members parameter from pool pool-Luna

Posted in F5, Python, Scripting | Tagged , , , , | Leave a comment

[Tufin]How to send access request using REST API

The structure of the XML is not properly explained in SWAGGER, swagger is a build in helper to browse the API calls within Tufin SecureChange itself. The example presented under Post Ticket is not very practical, the entire form is static, on this post i am sharing the structure required for you to post a request.

You can use postman to try it before writing logical scripts to do post method dynamically based on user’s input.

Ticket
The first line is <ticket>

Subject
The second line is <subject>
This is mandatory field, when you use the Tufin SecureChange to open a new request the top of the access request is a Subject field and you cannot remove it from the first step of the workflow.

Priority
The third is <priority>, just put Normal.

Workflow
This tells Tufin which workflow you want to use, the name must be the exact name as the workflow you created.
Example if my workflow name is Firewall_Automation. Then the entire workflow block looks like this:
<workflow>
<name>Firewall_Automation</name>
<uses_topology>true</uses_topology>
</workflow>

Steps
This indicate which step you are raising, if your first step in Tufin SecureChange workflow is create request, and the name of this step is Open request then this is where you specify, example:
<steps>
<step>
<name>Open request</name>
<tasks>
<task>
You will close the steps, step, tasks and task tags after the entire xml body is finished.

Field
Fields are items you put on your first step, you may have an access request field, a text box field, a text area field, you must indicate in your request the values of the field if the field is marked mandatory by you.

an example if you want to indicate in your xml body that you want to put a string in your textbox field, then it will look like this in your xml body.
<field xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xsi:type=”text_field”>
<name>Name of the text field</name>
<text>The actual words you want to put in the text</text>
</field>

If you have an access request field, and you require to do multiple access requests do this, in the below example my access request field name is Firewall Rule:
<field xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xsi:type=”multi_access_request”>
<name>Firewall Rule
<access_request>
If you have multiple access requests in your step you need to <access_request></access_request> multiple times.

So here is how an access request looks like, it needs to have source, destination, service and action.
For brevity this example has a source of 1.1.1.1/32 and destination is 2.2.2.2/32, service is TCP 4000, action allow.

A side track, Tufin SecureChange can only do Allow, it cannot do deny even the access request has this option, it cannot in most cases do drop. This drop action is only available for Cisco routers ACL.

See example of the above:
<fields>
<field xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xsi:type=”multi_access_request”>
<name>Firewall Rule</name>
<access_request>
<users>
<user>Any</user>
</users>
<sources>
<source type=”IP”>
<ip_address>1.1.1.1</ip_address>
<netmask>255.255.255.255</netmask>
</source>
</sources>
<destinations>
<destination type=”IP”>
<ip_address>2.2.2.2</ip_address>
<netmask>255.255.255.255</netmask>
</destination>
</destinations>
<services>
<service type=”PROTOCOL”>
<protocol>TCP</protocol>
<port>4000</port>
</service>
</services>
<action>Accept</action>
<labels/>
</access_request>
</field>
</fields>
</task>
</tasks>
</step>
</steps>
<comments/>
</ticket>

You can replace the <netmask>255.255.255.255</netmask> with <cidr>32</cidr>

Also take note on capitals see this one:
<protocol>TCP</protocol> must be TCP, not tcp, if you do not capitalized all protocol you will get a Status 400 bad request response from Tufin SecureChange

Understanding the request structure can help you to think of a method to do scripting based on dynamic inputs from users.

Consider this algorithm in powershell, the strategy is to iterate the unknown number of source address:

function GatherSrcHosts($new_host){
$src_addr = @" 
<source type="IP">
    <ip_address>$new_server</ip_address>
    <netmask>255.255.255.255</netmask>
</source>
"@
return $src_addr + "`r"
}

while($true)
{
    if($ws.Range("A" + $a).Text -ne "") # collect until an empty cell.
    {
        $srcHosts += $ws.Range("A" + $a).Text
    }
    else
    {
        break
    }
    $a += 1
}
foreach($srcHost in $srcHosts) 
{
    $src += [System.Net.DNS]::GetHostEntry($srcHost).AddressList.IPAddressToString
}

foreach($src_ip in $src) {
    $src_collections += GatherSrcHosts($src_ip)
}

You can do like this for service, and destination, in real life we never know what addresses and ports that user will request, I hope Tufin will provide more programming examples to help their customers to make it through with using their REST APIs. The example in swagger is simply not practical user like me has no idea how to start off.

i hope this post benefits people who use Tufin SecureChange as their firewall rule automation engine.

Posted in General stuffs | Tagged , , | Leave a comment

[python]Using F5 iControl API without F5-SDK

I am experimenting myself, I have not worked with json type before, I worked frequently with the xml type, I will need to learn how to parse json type data to get meaningful output..

here’s the sample code to test on GET.

import requests, logging

"""
Reference: 
https://codereview.stackexchange.com/questions/123571/rest-api-calls-to-big-ip-ltm-to-get-the-status-of-pool-members
https://devcentral.f5.com/wiki/iControlREST.Python-Virtual-Server-Pool-Create.ashx
"""

# Global parameters should be defined here
# credential
USERNAME = "admin"
PASSWORD = "121278"

# Bigip appliance address, use an array to store it
BIGIP_ADDRESSES = ["192.168.1.11"]

# Bigip LTM path, from this path we can cocatenate pool, member, and virtual server
BIGIP_LTM = "/mgmt/tm/ltm"

# Pools, use a dictionary so that the pool name can be reference to an index.
POOLS = {}

# Pool members (nodes), use an array to store the address with ports.
POOL_MEMBERS = []

# Log filename
LOG_FILENAME = "bigip_script.log"

# Logging configuration
logging.basicConfig(filename=LOG_FILENAME, format="%(asctime)s %(levelname)s:%(message)s",
                    level=logging.INFO)

# Package the session for bigip
bigip = requests.session()
bigip.auth = requests.auth.HTTPBasicAuth(USERNAME,PASSWORD)
# Specify the request header
bigip.headers.update({"Content-Type" : "application/json"})
# ignoring certificate security warning, meaning do not check for valid bigip certificate
bigip.verify = False
logging.debug("Preparing for bigip REST API sessions...")

payload = {}

for bigip_address in BIGIP_ADDRESSES:
    try:
        response = bigip.get("https://" + bigip_address + BIGIP_LTM + "/pool")
    except Exception as e:
        logging.error(e)
        exit(1)

payload = response.json()

for item in payload['items']:
    print(item['name'])
Posted in F5, Python, Scripting | Tagged , , | Leave a comment