iControlREST
72 TopicsGenerate private key w/ CSR via iControl REST
Problem this snippet solves: Generate a private key w/ CSR How to use this snippet: To create a private key with a CSR via iControl REST: POST URL:https://10.1.1.165/mgmt/tm/sys/crypto/key Use the data below as your payload. For the name field, it must end in .key or you will get a false 404! Code : { "name":"www.testing.com.key", "commonName":"www.testing.com", "keySize":"4096", "keyType":"rsa-private", "options":[{"gen-csr":"www.testing.com"}], "organization":"Let It Snow Corp.", "ou":"Ice Engineering", "city":"Calhoun", "state":"AZ", "admin-email-address":"jerry@letit.snow", "email-address":"beth@letit.snow", "subject-alternative-name":"DNS:www.testing.com", "challenge-password":"myP4ssword" } Tested this on version: 13.01.9KViews3likes11CommentsCreating a tmsh script with iControl REST and using it to restart HTTPD
Problem this snippet solves: TMSH has the ability to create tcl scripts that can be used to run multiple commands and transactions. It is rare that you will want to create on with TMSH but there are a few cases where this may be desirable. One of these is to restart HTTPD, which is difficult to do from iControl REST because the REST API is running over HTTPD and the restart will not be clean. See K13292945. This Python script creates a tmsh script, and then runs it to restart HTTPD. How to use this snippet: Syntax is <program_name.py> host user password Sample output: ./rest_script_example.py10.155.117.12 admin admin Before httpd (pid 3186) is running... After httpd (pid 3289) is running... Code : #!/usr/bin/python #m.lloyd@f5.com #Makes tmsh script to restart HTTP #Syntax: host username password import json #allow python 2 and python 3 by loading the correct libraries. try: from http.client import BadStatusLine from urllib.parse import urlparse, urlencode from urllib.request import urlopen, Request from urllib.error import HTTPError except ImportError: from httplib import BadStatusLine from urlparse import urlparse from urllib import urlencode from urllib2 import urlopen, Request, HTTPError import ssl import sys import time #Internal calls will not verify certs so disable cert verification. ssl._create_default_https_context = ssl._create_unverified_context #Create request for token based authentication. This is in Bigip 12 and later: url = 'https://'+sys.argv[1]+'/mgmt/shared/authn/login' values = {'username' : sys.argv[2], 'password' : sys.argv[3], 'loginProviderName' : 'tmos'} values = json.dumps(values).encode('utf-8') Request(url,data=values) req = Request(url,data=values) req.add_header('Content-Type' , 'application/json') #Request authentication token. response = urlopen(req) #auth=result will be a json data structure. auth_result = response.read() #print (auth_result) #Json.loads makes an internal python data structure that is easier to extract auth token from json. #Now construct icontrol rest query for device-groups info. auth=json.loads(auth_result) token=(auth['token']['token']) #print(token) #Get current PID of HTTPD url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("Before") print(python_response["apiRawValues"]["apiAnonymous"]) #look for script with name to make sure that the script does not already exist url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' #urllib2 raises an exception with an HTTP 404 req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) try: response = urlopen(req,data=None) except HTTPError as err: if err.code==404: #print (err.code) #print("\nCreate cli script\n") #request create here url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"name":"example.tcl", "apiAnonymous": "proc script::init {} {\n}\n\nproc script::run {} {\n tmsh::run util bash -c 'killall -9 httpd' \n tmsh::start sys service httpd\n} \n\nproc script::help {} {\n}\n\nproc script::tabc {} {\n}\n"} values = json.dumps(values) response = urlopen(req,data=values) response_py=(json.load(response)) #print(json.dumps(response_py,sort_keys=True,indent=4)) #Now run script url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"kind":"tm:cli:script:runstate","command":"run"} values = json.dumps(values).encode('utf-8') # Killing HTTPD will abort the connection so catch the exception. try: urlopen(req,data=values) except BadStatusLine: pass #Wait for httpd to restart so you can query. time.sleep(5) #Get current PID of HTTPD after restart url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("After") print(python_response["apiRawValues"]["apiAnonymous"]) Tested this on version: 13.01.4KViews2likes2CommentsPowerShell module for the F5 LTM REST API
Problem this snippet solves: To report an issue with the F5-LTM or F5-BIGIP modules, please use the Issues sections of the GitHub repos (here and here) instead of commenting here. Thanks! This PowerShell module uses the iControlREST API to manipulate and query pools, pool members, virtual servers, and iRules. It aims to support version 11.5.1 and higher, and to conform to the schedule for technical support of versions, though this may eventually prove to become difficult. The module currently includes some functionality that, strictly speaking, is outside the scope of the LTM module. Hence, there is an active effort to wrap this LTM module into a larger BIG-IP module, and relocate that functionality elsewhere within that parent module, as well as expand the scope of functionality to include BIG-IP DNS (formerly GTM) and possibly other areas. Both the LTM module and the parent BIG-IP module are projects on github. Please use these projects to report any issues you discover. Thanks! The module contains the following functions. Add-iRuleToVirtualServer Add-iRuleToVirtualServer Add-PoolMember Add-PoolMonitor Disable-PoolMember Disable-VirtualServer Enable-PoolMember Enable-VirtualServer Get-CurrentConnectionCount (deprecated; use Get-PoolMemberStats | Select-Object -ExpandProperty 'serverside.curConns') Get-F5Session (will be deprecated in future versions. use New-F5Session) Get-F5Status Get-HealthMonitor Get-HealthMonitorType Get-iRule Get-iRuleCollection (deprecated; use Get-iRule) Get-Node Get-BIGIPPartition Get-Pool Get-PoolList (deprecated; use Get-Pool) Get-PoolMember Get-PoolMemberCollection (deprecated; use Get-PoolMember) Get-PoolMemberCollectionStatus Get-PoolMemberDescription (deprecated; use Get-PoolMember) Get-PoolMemberIP (deprecated; use Get-PoolMember) Get-PoolMembers (deprecated; use Get-PoolMember) Get-PoolMemberStats Get-PoolMemberStatus (deprecated; use Get-PoolMember) Get-PoolMonitor Get-PoolsForMember Get-StatusShape Get-VirtualServer Get-VirtualServeriRuleCollection (deprecated; use Get-VirtualServer | Where rules | Select -ExpandProperty rules) Get-VirtualServerList (deprecated; use Get-VirtualServer) Invoke-RestMethodOverride New-F5Session New-HealthMonitor New-Node New-Pool New-VirtualServer Remove-HealthMonitor Remove-iRule Remove-iRuleFromVirtualServer Remove-Pool Remove-PoolMember Remove-PoolMonitor Remove-ProfileRamCache Remove-Node Remove-VirtualServer Set-iRule Set-PoolLoadBalancingMode (deprecated; use Set-Pool) Set-PoolMemberDescription Set-Pool Set-VirtualServer Sync-DeviceToGroup Test-F5Session Test-Functionality Test-HealthMonitor Test-Node Test-Pool Test-VirtualServer How to use this snippet: To use the module, click 'Download Zip', extract the files, and place them in a folder named F5-LTM beneath your PowerShell modules folder. By default, this is %USERPROFILE%\Documents\WindowsPowerShell\Modules. The WindowsPowerShell and Modules folders may need to be created. You will most likely need to unblock the files after extracting them. Use the Unblock-File PS cmdlet to accomplish this. The Validation.cs class file (based on code posted by Brian Scholer) allows for using the REST API with LTM devices with self-signed SSL certificates. Nearly all of the functions require an F5 session object as a parameter, which contains the base URL for the F5 LTM and a credential object for a user with privileges to manipulate the F5 LTM via the REST API. Use the New-F5session function to create this object. This function expects the following parameters: The name or IP address of the F5 LTM device A credential object for a user with rights to use the REST API An optional TokenLifespan value for extending the life of the authentication token past the default 20 minutes You can create a credential object using Get-Credential and entering the username and password at the prompts, or programmatically like this: $secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential "username", $secpasswd Thanks to Kotesh Bandhamravuri and his blog entry for this snippet. There is a function called Test-Functionality that takes an F5Session object, a new pool name, a new virtual server, an IP address for the virtual server, and a computer name as a pool member, and validates nearly all the functions in the module. I've also contributed this code sample for how to gather some basic info about your LTM with this PS module. The module has been tested on: 11.5.1 Build 8.0.175 Hotfix 8 and later 11.6.0 Build 5.0.429 Hotfix 4 and later 12.0 / 12.1 13.0 Code : https://github.com/joel74/POSH-LTM-Rest Tested this on version: 11.519KViews2likes150CommentsDownload a BIG-IP UCS archive with "curl".
Problem this snippet solves: Download a BIG-IP UCS archive using the program "curl" and verifies the output file's signature. Tested on 13.1.1. How to use this snippet: Edit the code to input the hostname of your F5 UI, admin credentials, source UCS file name (defaults to config.ucs), and the output file name. Code : #!/bin/bash # # Download a UCS archive (across a stable network) with curl. # #------------------------------------------------------------------------- F5_HOST='myhost.example.com' CREDENTIALS='admin:admin' FINAL_FILE='/tmp/config.ucs' ARCHIVE_NAME_ON_SERVER='config.ucs' DEBUG='' #------------------------------------------------------------------------- # # Get the md5 checksum for the archive. # #------------------------------------------------------------------------- ARCHIVE_CHECKSUM=$(curl -sku $CREDENTIALS -X POST -H "Content-type: application/json" \ -d "{\"command\":\"run\", \"utilCmdArgs\": \"-c '/usr/bin/md5sum /var/local/ucs/$ARCHIVE_NAME_ON_SERVER'\"}" \ https://$F5_HOST/mgmt/tm/util/bash | awk -F':' '{print $NF}' | awk -F'"' '{ print $2 }' | awk '{print $1}') [ -z "$ARCHIVE_CHECKSUM" ] && echo "Failed to get archive signature. Aborting." && exit 1 [ ! -z "$DEBUG" ] && echo "Archive checksum: $ARCHIVE_CHECKSUM" #------------------------------------------------------------------------- # # Find out the size of the archive and the size of the data packet. # #------------------------------------------------------------------------- Content_Range=$(curl -I -kv -u $CREDENTIALS -H 'Content-Type: application/json' -X GET "https://$F5_HOST/mgmt/shared/file-transfer/ucs-downloads/$ARCHIVE_NAME_ON_SERVER" 2>/dev/null | grep "Content-Range: " | cut -d ' ' -f 2) FIRST_CONTENT_RANGE=$(echo -n $Content_Range | cut -d '/' -f 1 | tr -d '\r') [ ! -z "$DEBUG" ] && echo -n "FIRST_CONTENT_RANGE: " [ ! -z "$DEBUG" ] && echo $FIRST_CONTENT_RANGE NUMBER_OF_LAST_BYTE=$(echo -n $FIRST_CONTENT_RANGE | cut -d '-' -f 2) [ ! -z "$DEBUG" ] && echo -n "NUMBER_OF_LAST_BYTE: " [ ! -z "$DEBUG" ] && echo $NUMBER_OF_LAST_BYTE INITIAL_CONTENT_LENGTH=$NUMBER_OF_LAST_BYTE CONTENT_LENGTH=$(($NUMBER_OF_LAST_BYTE+1)) [ ! -z "$DEBUG" ] && echo -n "CONTENT_LENGTH: " [ ! -z "$DEBUG" ] && echo $CONTENT_LENGTH DFILE_SIZE=$(echo -n $Content_Range | cut -d '/' -f 2 | tr -d '\r' ) [ ! -z "$DEBUG" ] && echo -n "DFILE_SIZE: " [ ! -z "$DEBUG" ] && echo $DFILE_SIZE LAST_END_BYTE=$((DFILE_SIZE-1)) CUMULATIVE_NO=0 [ ! -z "$DEBUG" ] && echo "CUMULATIVE_NO: $CUMULATIVE_NO" SEQ=0 LAST=0 #------------------------------------------------------------------------- # # Clean up: Remove the previous output file. # #------------------------------------------------------------------------- /bin/rm $FINAL_FILE 2>/dev/null #------------------------------------------------------------------------- # # Get the archive file. # #------------------------------------------------------------------------- while true do if [ $LAST -gt 0 ]; then [ ! -z "$DEBUG" ] && echo 'End of run reached.' break fi if [ $SEQ -eq 0 ]; then NEXT_RANGE=$FIRST_CONTENT_RANGE CUMULATIVE_NO=$NUMBER_OF_LAST_BYTE CONTENT_LENGTH=$INITIAL_CONTENT_LENGTH else START_BYTE=$(($CUMULATIVE_NO+1)) END_BYTE=$(($START_BYTE + $CONTENT_LENGTH)) if [ $END_BYTE -gt $LAST_END_BYTE ]; then [ ! -z "$DEBUG" ] && echo "END_BYTE greater than LAST_END_BYTE: $END_BYTE:$LAST_END_BYTE" LAST=1 let END_BYTE=$LAST_END_BYTE [ ! -z "$DEBUG" ] && echo "Getting the last data packet." fi NEXT_RANGE="${START_BYTE}-${END_BYTE}" CUMULATIVE_NO=$END_BYTE fi [ ! -z "$DEBUG" ] && echo "NEXT_RANGE: $NEXT_RANGE" let SEQ+=1 [ ! -z "$DEBUG" ] && echo "SEQ: $SEQ" OUTPUT_FILE_NAME="/tmp/$$_downloaded_ucs_archive_file_part_$SEQ"; curl -H "Content-Range: ${NEXT_RANGE}/${DFILE_SIZE}" -s -k -u $CREDENTIALS -H 'Content-Type: application/json' -X GET "https://$F5_HOST/mgmt/shared/file-transfer/ucs-downloads/$ARCHIVE_NAME_ON_SERVER" -o $OUTPUT_FILE_NAME cat $OUTPUT_FILE_NAME >> $FINAL_FILE /bin/rm $OUTPUT_FILE_NAME [ ! -z "$DEBUG" ] && echo "End of loop $SEQ" done #------------------------------------------------------------------------- # # Verify downloaded file. # #------------------------------------------------------------------------- FINAL_FILE_CHECKSUM=$(/usr/bin/md5sum $FINAL_FILE | awk '{print $1}') if [ "$FINAL_FILE_CHECKSUM" == "$ARCHIVE_CHECKSUM" ]; then echo "Download completed and verified." else echo "Downloaded file has incorrect checksum." exit 1 fi # END -------------------------------------------------------------------- Tested this on version: 13.01.5KViews2likes5CommentsiControlREST Auth Token and Transaction Example (Postman)
Problem this snippet solves: This is an example set for iControlREST which generates an Authentication Token and a Transaction session to add a new Data Group. This is only a single change but you can add many changes into the Transaction before VALIDATING and committing them. Steps taken: Get Auth Token - Request to generate a new Authentication Token and saves into the Environment variable X-F5-Auth-Token Extend Token Timeout - Increases the timeout value of the Auth Token, not always needed but good if you are running the command manually Get New Transaction - Request to generate a new Transaction session and saves into the Environment variable Coordination-Id POST new DG in Transaction - Creates a new Data Group Get Transaction Commands - Optional request to list all the commands and the order in the transaction Commit Transaction - Sends VALIDATING request to validate and commit the commends Get DG test - Optional to get the Data Group to confirm it has been created Find more information about iControlREST Transactions here https://devcentral.f5.com/s/articles/demystifying-icontrol-rest-part-7-understanding-transactions-21404 and in the user guides https://clouddocs.f5.com/api/icontrol-rest/ How to use this snippet: Download and install Postman https://www.getpostman.com/downloads/ Save the below JSON to a file and import as a new Postman Collection (see https://learning.getpostman.com/docs/postman/collections/intro_to_collections/ and https://learning.getpostman.com/docs/postman/collections/data_formats/#importing-postman-data). Finally setup a new Environment (https://learning.getpostman.com/docs/postman/environments_and_globals/manage_environments/) within Postman and ensure you have the following elements: hostIP - the Management IP of the F5 BIG-IP system hostName - the Hostname of the F5 BIG-IP system f5user - the username used to generate an Authentication Token f5pass - the password used to generate an Authentication Token X-F5-Auth-Token - leave blank will auto populate Coordination-Id - leave blank will auto populate e.g. Then you can run the Postman collection one request at a time or run via Postman's Collection Runner (https://learning.getpostman.com/docs/postman/collection_runs/using_environments_in_collection_runs). Code : { "info": { "_postman_id": "67195ea2-5ac0-4599-a650-5951b1bc1184", "name": "iControl Transaction Example", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "Get Auth Token", "event": [ { "listen": "test", "script": { "id": "6e3f6680-4199-4c4a-a210-272b4d2eef38", "exec": [ "tests[\"Status code is 200\"] = responseCode.code === 200;", "var jsonData = JSON.parse(responseBody);", "postman.setEnvironmentVariable(\"X-F5-Auth-Token\", jsonData.token.name);", "", "" ], "type": "text/javascript" } } ], "request": { "method": "POST", "header": [ { "key": "Host", "type": "text", "value": "{{hostName}}" }, { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\r\n\t\"username\":\"{{f5user}}\",\r\n\t\"password\":\"{{f5pass}}\",\r\n\t\"loginProviderName\": \"tmos\"\r\n}" }, "url": { "raw": "https://{{hostIP}}/mgmt/shared/authn/login", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "shared", "authn", "login" ] } }, "response": [] }, { "name": "Extend Token Timeout Copy", "event": [ { "listen": "test", "script": { "id": "3bcdcdc6-fcad-46db-b9c0-4d7a8e8e1a69", "exec": [ "var jsonData = JSON.parse(responseBody);", "tests[\"Status code is 200\"] = responseCode.code === 200;", "tests[\"Token has been set\"] = jsonData.timeout == 36000;", "tests[\"Token is valid\"] = jsonData.userName === postman.getEnvironmentVariable(\"f5user\");", "" ], "type": "text/javascript" } } ], "request": { "method": "PATCH", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "Content-Type", "value": "application/json" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}" } ], "body": { "mode": "raw", "raw": "{\n\t\"timeout\":\"36000\"\n}" }, "url": { "raw": "https://{{hostIP}}/mgmt/shared/authz/tokens/{{X-F5-Auth-Token}}", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "shared", "authz", "tokens", "{{X-F5-Auth-Token}}" ] } }, "response": [] }, { "name": "Get New Transaction", "event": [ { "listen": "test", "script": { "id": "cb847d93-2c3a-4990-8242-020d95532be6", "exec": [ "var jsonRsponse = JSON.parse(responseBody)", "pm.environment.set(\"Coordination-Id\", jsonRsponse.transId);", "", "" ], "type": "text/javascript" } } ], "request": { "auth": { "type": "noauth" }, "method": "POST", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "content-type", "value": "application/json", "type": "text" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}", "type": "text" } ], "body": { "mode": "raw", "raw": "{}" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/transaction/", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "transaction", "" ] } }, "response": [] }, { "name": "POST new DG in Transaction", "request": { "auth": { "type": "noauth" }, "method": "POST", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "content-type", "value": "application/json", "type": "text" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}", "type": "text" }, { "key": "X-F5-REST-Coordination-Id", "value": "{{Coordination-Id}}", "type": "text" } ], "body": { "mode": "raw", "raw": "{\n \"partition\": \"Common\",\n \"name\": \"url_filter_dg\",\n \"records\": [\n {\n \"name\": \"/data\",\n \"data\": \"Allow\"\n },\n {\n \"name\": \"/filter\",\n \"data\": \"Block\"\n },\n {\n \"name\": \"/hello\",\n \"data\": \"Black\"\n },\n {\n \"name\": \"/login\",\n \"data\": \"Allow\"\n }\n ],\n \"type\":\"string\"\n}" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/ltm/data-group/internal", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "ltm", "data-group", "internal" ] } }, "response": [] }, { "name": "PUT DG in Transaction", "request": { "auth": { "type": "basic", "basic": [ { "key": "password", "value": "admin", "type": "string" }, { "key": "username", "value": "admin", "type": "string" } ] }, "method": "PUT", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "content-type", "value": "application/json", "type": "text" }, { "key": "X-F5-REST-Coordination-Id", "value": "{{Coordination-Id}}", "type": "text" } ], "body": { "mode": "raw", "raw": "{\n \"records\": [\n {\n \"name\": \"/data\",\n \"data\": \"Allow\"\n },\n {\n \"name\": \"/filter\",\n \"data\": \"Block\"\n },\n {\n \"name\": \"/hello\",\n \"data\": \"Allow\"\n },\n {\n \"name\": \"/login\",\n \"data\": \"Allow\"\n }\n ]\n}" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/ltm/data-group/internal/~common~url_filter_dg", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "ltm", "data-group", "internal", "~common~url_filter_dg" ] } }, "response": [] }, { "name": "Get Transaction Commands", "event": [ { "listen": "test", "script": { "id": "cb847d93-2c3a-4990-8242-020d95532be6", "exec": [ "", "", "" ], "type": "text/javascript" } } ], "protocolProfileBehavior": { "disableBodyPruning": true }, "request": { "auth": { "type": "noauth" }, "method": "GET", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "content-type", "value": "application/json", "type": "text" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}", "type": "text" } ], "body": { "mode": "raw", "raw": "{}" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/transaction/{{Coordination-Id}}/commands", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "transaction", "{{Coordination-Id}}", "commands" ] } }, "response": [] }, { "name": "Commit Transaction", "event": [ { "listen": "test", "script": { "id": "8308b285-b26b-4ddf-8ea9-e4f420cccd42", "exec": [ "var jsonResponse = JSON.parse(responseBody)", "", "pm.test(\"Transaction status is COMPLETED\", function () {", "", " pm.expect(jsonResponse.state == \"COMPLETED\");", "});" ], "type": "text/javascript" } } ], "request": { "auth": { "type": "noauth" }, "method": "PATCH", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "content-type", "value": "application/json", "type": "text" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}", "type": "text" }, { "key": "X-F5-REST-Coordination-Id", "value": "1557741207510527", "type": "text", "disabled": true } ], "body": { "mode": "raw", "raw": "{ \"state\":\"VALIDATING\" }" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/transaction/{{Coordination-Id}}", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "transaction", "{{Coordination-Id}}" ] } }, "response": [] }, { "name": "Get DG test", "request": { "auth": { "type": "noauth" }, "method": "GET", "header": [ { "key": "Host", "value": "{{hostName}}", "type": "text" }, { "key": "X-F5-Auth-Token", "value": "{{X-F5-Auth-Token}}", "type": "text" } ], "body": { "mode": "raw", "raw": "" }, "url": { "raw": "https://{{hostIP}}/mgmt/tm/ltm/data-group/internal/~common~url_filter_dg", "protocol": "https", "host": [ "{{hostIP}}" ], "path": [ "mgmt", "tm", "ltm", "data-group", "internal", "~common~url_filter_dg" ] } }, "response": [] } ] } Tested this on version: No Version Found1.1KViews2likes0CommentsGo library to manage BIG-IP iControl REST API
Problem this snippet solves: This library provides necessary structs and functions to manage the whole REST API. Some REST Calls may require BIG-IP v12.1.x to work properly. How to use this snippet: f5-rest-client implements a REST client to query the F5 BIG-IP iControl REST API. Installation go get -u github.com/e-XpertSolutions/f5-rest-client/f5 Available authentication methods Basic authentication f5Client, err := f5.NewBasicClient(base_url, username, password) Token based authentication f5Client, err := f5.NewTokenClient(base_url, username, password, login_provider_name, skip_ssl_verification) Usage // Copyright 2017 e-Xpert Solutions SA. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "encoding/json" "log" "github.com/e-XpertSolutions/f5-rest-client/f5" "github.com/e-XpertSolutions/f5-rest-client/f5/net" ) func sexyPrint(label string, a interface{}) { j, err := json.MarshalIndent(a, "", " ") if err != nil { log.Fatal(err) } log.Print("DEBUG ", label, ":\n", string(j)) } func main() { // 1) Basic Authentication f5Client, err := f5.NewBasicClient("https://127.0.0.1", "admin", "admin") // 2) Token Based Authentication // f5Client, err := f5.NewTokenClient("https://127.0.0.1", "admin", "admin", "tmos", true) if err != nil { log.Fatal(err) } f5Client.DisableCertCheck() netClient := net.New(f5Client) self, err := netClient.Self().ListAll() if err != nil { log.Fatal(err) } sexyPrint("SelfIP List:", self) } FEATURES Basic authentication Token based authentication Manage Virtual Server, pool, node, irules, monitors Manage Cluster Management Manage interfaces, vlan, trunk, self ip, route, route domains Manage virtualization features (/vcmp) Manage system related stuffs Add Helper functions to enable, disable or force a node offline Add Helper functions to enable or disable a Virtual Server List expiring certificates List expired certificates Transaction support [new] Manage DNS and global load balancing servers (/gtm) [new] Add support for Stats retrieval on node, pool, virtual and profiles ROADMAP Add support for authentication through external providers Manage access policies (/apm) Manage security (/security) Manage analytics configuration (/analytics) Add support for results pagination Add support for API versioning Add support for new API endpoints coming in v13 Examples Transactions - Create a simple HTTP service f5Client, err := f5.NewBasicClient("https://127.0.0.1", "admin", "admin") if err != nil { log.Fatal(err) } f5Client.DisableCertCheck() // Start new transaction. tx, err := f5Client.Begin() if err != nil { log.Fatal(err) } ltmClient := ltm.New(tx) // Create a HTTP monitor log.Print("Create a HTTP monitor") monitorConfig := ltm.MonitorHTTPConfig{ Name: "http_monitor_" + tx.TransactionID(), Send: "GET / HTTP/1.0\r\n\r\n", Recv: "Hello", } if err := ltmClient.MonitorHTTP().Create(monitorConfig); err != nil { log.Fatal(err) } // Create a Pool log.Print("Create a pool") poolConfig := ltm.PoolConfig{ Name: "pool_" + tx.TransactionID(), Monitor: "/Common/http_monitor_" + tx.TransactionID(), Members: []string{"10.1.10.10:80", "10.1.10.11:80"}, } if err := ltmClient.Pool().Create(poolConfig); err != nil { log.Fatal(err) } // Create a Virtual Server log.Print("Create a Virtual Server") vsConfig := ltm.VirtualServerConfig{ Name: "vs_http_" + tx.TransactionID(), Destination: "10.1.20.130:80", IPProtocol: "tcp", Pool: "pool_" + tx.TransactionID(), SourceAddressTranslation: ltm.SourceAddressTranslation{ Type: "automap", }, Profiles: []string{ "tcp-mobile-optimized", "http", }, } if err := ltmClient.Virtual().Create(vsConfig); err != nil { log.Fatal(err) } // Commit to make the changes persistent. if err := tx.Commit(); err != nil { log.Fatal(err) } List SSL Certificates sysClient := sys.New(f5Client) certs, err := sysClient.FileSSLCert().ListAll() if err != nil { log.Fatal(err) } sexyPrint("Certificates", certs) List expired SSL Certificates sysClient := sys.New(f5Client) certs, err := sysClient.FileSSLCert().ListExpired() if err != nil { log.Fatal(err) } sexyPrint("Expired Certificates", certs) List expiring SSL Certificates sysClient := sys.New(f5Client) // ListExpiring(number_of_seconds) certs, err := sysClient.FileSSLCert().ListExpiring(60 * 60 * 24 * 15) if err != nil { log.Fatal(err) } sexyPrint("Expiring Certificates", certs) Contributing We appreciate any form of contribution (feature request, bug report, pull request, ...). We have no special requirements for Pull Request, just follow the standard GitHub way. License The sources are release under a BSD 3-Clause License. The full terms of that license can be found in LICENSE file of this repository. Code : https://github.com/e-XpertSolutions/f5-rest-client Tested this on version: 11.51.3KViews1like3CommentsUpload small files (certs/keys/etc) to BIG-IP via iControl REST (base64 encoded using cli script)
Problem this snippet solves: I had a need to upload SSL certificates and keys to the BIG-IP in a simple manner that was fast. I did so by creating a cli script that would accept a filename, base64 encoded file and the file type (ascii/binary), then would write out the file to disk. How to use this snippet: curl -sk -u username:password -X POST -H 'Content-Type: application/json'https://PRIMARY_F5/mgmt/tm/cli/script-d '{"command":"run","utilCmdArgs":"file_creation [filename] [filetype] [file_base64encoded"}' Code : create cli script fileupload { proc script::init {} { } proc script::run {} { package require base64 set filename [lindex $tmsh::argv 1] set filetype [lindex $tmsh::argv 2] set contents [lindex $tmsh::argv 3] if { [file exists $filename] == 0} { if { $filetype == "binary"} { set result [::base64::decode $contents] set finalfile [open $filename "w"] fconfigure $finalfile -encoding binary puts -nonewline $finalfile [::base64::decode $contents] close $finalfile return 0 } if { $filetype == "ascii"} { set result [::base64::decode $contents] set finalfile [open $filename "w"] fconfigure $finalfile puts -nonewline $finalfile [::base64::decode $contents] close $finalfile return 0 } } if { [file exists $filename] == 1} { puts "File already exists" error "File already exists" return 1 } } proc script::help {} { } proc script::tabc {} { } total-signing-status not-all-signed } Tested this on version: 12.1373Views1like0CommentsBacking up Master Keys via iControl REST
Problem this snippet solves: Having a backup copy of a chassis master key can be valuable during a recovery event. For organizations with large deployments, automating the process certainly helps. How to use this snippet: To back up the magic key via API, use the bash util command URI with the proper payload: Sample URI: https://192.168.1.100/mgmt/tm/util/bash Payload: {"command":"run","utilCmdArgs":"-c 'f5mku -K'"} Response: {"kind":"tm:util:bash:runstate","command":"run","utilCmdArgs":"-c 'f5mku -K'","commandResult":"ZFLI5n83NuetlE9A+bYqwg==\n"} The master key is the commandResult field, minus the trailing \n. Code : curl -sku admin:admin -H "content-type: application/json" -X POST https://192.168.1.100/mgmt/tm/util/bash -d {"command":"run","utilCmdArgs":"-c 'f5mku -K'"} Tested this on version: 13.0470Views1like0CommentsPython script for updating data group
Problem this snippet solves: Updating data group via Python. From the article: Routing HTTP by request headers Currently when you need to update a data group via iControl REST it requires you to retrieve a copy of all of the records, make your changes, and then re-submit all the records to make any changes. The following Python script takes care of the process of extracting all of the records, turning them into a dictionary, and then allowing the data group to be updated by passing a dictionary. How to use this snippet: This code can be imported as a class or run from the command-line. From the command-line: % ./cgp.py add www.mycompany.example 10.10.1.11:80 added: www.mycompany.example % ./cgp.py get www.mycompany.example www.mycompany.example: 10.10.1.11:80 % ./cgp.py update www.mycompany.example 10.10.1.11:8000 updated: www.mycompany.example % ./cgp.py del www.mycompany.example deleted: www.mycompany.example From code from cgp import CGP BIGIP_ADDRESS = '[BIGIP MGMT HOST]' BIGIP_USER = '[admin user]' BIGIP_PASS = '[admin password]' DATA_GROUP = '~Common~[data-group name]' cgp = CGP(BIGIP_ADDRESS, BIGIP_USER, BIGIP_PASS) # get record data = cgp.get_data_group(DATA_GROUP) # add record data['www.mycompany.example'] = '10.10.1.10:80' cgp.set_data_group(DATA_GROUP, data) # update record data = cgp.get_data_group(DATA_GROUP) data['www.mycompany.example'] = '10.10.1.10:80' cgp.set_data_group(DATA_GROUP, data) # delete record data = cgp.get_data_group(DATA_GROUP) del data['www.mycompany.example'] cgp.set_data_group(DATA_GROUP, data) Code : #!/usr/bin/python # # Chen Gateway Protocol # # Example of using iControl REST # From: https://devcentral.f5.com/s/articles/routing-http-by-request-headers # # Requires iControl REST > 11.5.0 # # The following script allows one to dynamically update a data-group via # iControl REST # import requests import json BIGIP_ADDRESS = '[BIGIP MGMT HOST]' BIGIP_USER = '[admin user]' BIGIP_PASS = '[admin password]' DATA_GROUP = '~Common~[data-group name]' class CGP(object): def __init__(self, host, user, passwd): self.host = host self.user = user self.passwd = passwd self.mgmt_url = "https://%s/mgmt/tm" %(host) self.session = requests.session() self.session.auth = (user, passwd) self.session.verify = False self.session.headers.update({'Content-Type':'application/json'}) def get_data_group(self, datagroup): req_url = "%s/ltm/data-group/internal/%s" %(self.mgmt_url, datagroup) req = self.session.get(req_url) return dict([(a['name'],a['data']) for a in req.json()['records']]) "return contents of data-group as dictionary" def set_data_group(self, datagroup, data): req_url = "%s/ltm/data-group/internal/%s" %(self.mgmt_url, datagroup) records = {'records': [{'data':r[1],'name':r[0]} for r in data.items()]} req = self.session.put(req_url,data = json.dumps(records) ) "update contents of data-group as dictionary" if __name__ == "__main__": import argparse import sys parser = argparse.ArgumentParser() parser.add_argument('action') parser.add_argument('host_header') parser.add_argument('node_ip_port',nargs='?') args = parser.parse_args() cgp = CGP(BIGIP_ADDRESS, BIGIP_USER, BIGIP_PASS) data = cgp.get_data_group(DATA_GROUP) if args.action == 'del': if args.host_header in data: del data[args.host_header] print "deleted:", args.host_header else: print "error: %s does not exist" %(args.host_header) sys.exit(1) elif args.action == 'add': if args.host_header in data: print "error: %s already exists" %(args.host_header) sys.exit(1) else: data[args.host_header] = args.node_ip_port print "added: %s" %(args.host_header) elif args.action == 'update': if args.host_header in data: data[args.host_header] = args.node_ip_port print "updated:", args.host_header else: print "error: %s does not exist" %(args.host_header) sys.exit(1) elif args.action == 'get': print "%s: %s" %(args.host_header,data.get(args.host_header,"DOES NOT EXIST")) cgp.set_data_group(DATA_GROUP, data) Tested this on version: 11.51.2KViews1like5Comments