Create New ASM Policy with iControl REST

Problem this snippet solves:

Proof of concept code to show how to create a new ASM policy using iControl REST.

How to use this snippet:

./create_new_asm_policy.py usage: create_new_asm_policy.py [-h] --bigip BIGIP --user USER --policyname POLICYNAME [--language {utf-8,auto-detect}] [--protocolindependence {false,true}] [--caseinsensitive {false,true}] [--enforcement {blocking,transparent}] [--template {Fundamental,Comprehensive}] [--virtual [VIRTUAL [VIRTUAL ...]]] [--learningmode {automatic,manual,disabled}]

Code :

#!/usr/bin/python

# create_new_asm_policy.py
# Author: Chad Jenison (c.jenison at f5.com)
# Version 1.0
#
# Script that creates new ASM policy using iControl REST and allows some customization of initial optionso
# --virtual option allows attaching policy to one or more virtuals

import argparse
import sys
import requests
import json
import getpass

#Setup command line arguments using Python argparse
parser = argparse.ArgumentParser(description='A tool to parse web log files and add them to an ASM security policy')
parser.add_argument('--bigip', '-b', help='IP or hostname of BIG-IP Management or Self IP', required=True)
parser.add_argument('--user', '-u', help='username to use for authentication', required=True)
parser.add_argument('--policyname', '-p', help='ASM security policy name', required=True)
parser.add_argument('--language', '-l', help='Application Language', default='utf-8', choices=['utf-8','auto-detect'])
parser.add_argument('--protocolindependence', '-prot', help='Protocol Independence (false = distinguish between HTTP and HTTPS; true = treat HTTP and HTTPS equivalently)', default='false', choices=['false','true'])
parser.add_argument('--caseinsensitive', '-c', help='Case Insensitive', default='false', choices=['false','true'])
parser.add_argument('--enforcement', '-e', help='Enforcement Mode - Blocking or Transparent', default='blocking', choices=['blocking','transparent'])
parser.add_argument('--template', '-t', choices=['Fundamental', 'Comprehensive'])
parser.add_argument('--virtual', '-v', nargs='*', help='Virtual Server(s) to attach to (with full path [e.g. /Common/test])')
parser.add_argument('--learningmode', '-m', choices=['automatic', 'manual', 'disabled'])

args = parser.parse_args()

contentTypeJsonHeader = {'Content-Type': 'application/json'}

#adapted from https://devcentral.f5.com/s/articles/demystifying-icontrol-rest-6-token-based-authentication 
def get_auth_token():
    payload = {}
    payload['username'] = args.user
    payload['password'] = passwd
    payload['loginProviderName'] = 'tmos'
    authurl = 'https://%s/mgmt/shared/authn/login' % args.bigip
    token = bip.post(authurl, headers=contentTypeJsonHeader, auth=(args.user, passwd), data=json.dumps(payload)).json()['token']['token']
    return token

def get_asm_policy_id_from_name(name):
    policies = bip.get('%s/asm/policies/' % (url_base)).json()
    for policy in policies['items']:
        if policy['name'] == name:
    id = policy['id']
    return id

url_base = ('https://%s/mgmt/tm' % (args.bigip))
user = args.user
passwd = getpass.getpass("Password for " + user + ":")
bip = requests.session()
bip.verify = False
requests.packages.urllib3.disable_warnings()
authtoken = get_auth_token()
authheader = {'X-F5-Auth-Token': authtoken}
bip.headers.update(authheader)

policyTemplates = bip.get('%s/asm/policy-templates/' % (url_base)).json()
for policyTemplate in policyTemplates['items']:
    if policyTemplate['title'] == 'Fundamental' and args.template == 'Fundamental':    
        policyTemplateId = policyTemplate['id'] 
    if policyTemplate['title'] == 'Comprehensive' and args.template == 'Comprehensive':    
        policyTemplateId = policyTemplate['id'] 

# combine two Python Dicts (our auth token and the Content-type json header) in preparation for doing POSTs
postHeaders = authheader
postHeaders.update(contentTypeJsonHeader)

policyPayloadDict = {'name':args.policyname, 'caseInsensitive':args.caseinsensitive, 'enforcementMode':args.enforcement, 'applicationLanguage':args.language, 'protocolIndependent':args.protocolindependence}
if args.template is not None:
    policyPayloadDict.update({'templateReference': policyTemplateId})

if args.virtual is not None:
    print ('Virtual(s) to add: %s' % (args.virtual))
    policyPayloadDict.update({'virtualServers' : args.virtual })

newPolicyPayload = json.dumps(policyPayloadDict)

addPolicy = bip.post('%s/asm/policies/' % (url_base), headers=postHeaders, data = newPolicyPayload)
if addPolicy.status_code == 201:
    policyId = get_asm_policy_id_from_name(args.policyname)
    print ('Successfully Created Policy: %s - policyId: %s' % (args.policyname, policyId))
else:
    print ('Unsuccessful attempt to create policy: %s - Status Code: %s' % (args.policyname, addPolicy.status_code))
    print ('Body: %s' % (addPolicy.content))

if args.learningmode is not None:
    policyBuilderPatchDict = {'learningMode': args.learningmode}
    updateLearning = bip.patch('%s/asm/policies/%s/policy-builder' % (url_base, policyId), headers=postHeaders, data = json.dumps(policyBuilderPatchDict))
    print ('Set Learning Mode to: %s on policy: %s' % (args.learningmode, args.policyname))

Tested this on version:

13.0
Published Feb 19, 2018
Version 1.0
  • Is this already developed in f5 common Python? Because I am trying and it does not work to me

     

  • Very handy script. Thank you for this!

     

    I tweaked a tad to activate the policies if needed. I'll also be adding the ability to loop through a list of policy names/Templates to automate a large scale deployment I'm working on.

     

    This saved me a lot of work on my own.

     

  • Hi,

     

    I'm running v14.1 and it looks like template option is not working anymore. All created ASM policies use Fundamental template instead of the one mentioned during the policy creation. Can you confirm that the script is valid for v14 and v15 ?