iApps
97 TopicsComplete F5 Automated Backup Solution
Problem this snippet solves: Hi all, Often I've been scouring the devcentral fora and codeshares to find that one piece of handywork that will drastically simplify my automated backup needs on F5 devices. Based on the works of Jason Rahm in his post "Third Time's the Charm: BIG-IP Backups Simplified with iCall" on the 26th of June 2013, I went ahead and created my own iApp that pretty much provides the answers for all my backup-needs. Here's a feature list of this iApp: It allows you to choose between both UCS or SCF as backup-types. (whilst providing ample warnings about SCF not being a very good restore-option due to the incompleteness in some cases) It allows you to provide a passphrase for the UCS archives (the standard GUI also does this, so the iApp should too) It allows you to not include the private keys (same thing: standard GUI does it, so the iApp does it too) It allows you to set a Backup Schedule for every X minutes/hours/days/weeks/months or a custom selection of days in the week It allows you to set the exact time, minute of the hour, day of the week or day of the month when the backup should be performed (depending on the usefulness with regards to the schedule type) It allows you to transfer the backup files to external devices using 4 different protocols, next to providing local storage on the device itself * SCP (username/private key without password) SFTP (username/private key without password) FTP (username/password) SMB (using smbclient, with username/password) Local Storage (/var/local/ucs or /var/local/scf) It stores all passwords and private keys in a secure fashion: encrypted by the master key of the unit (f5mku), rendering it safe to store the backups, including the credentials off-box It has a configurable automatic pruning function for the Local Storage option, so the disk doesn't fill up (i.e. keep last X backup files) It allows you to configure the filename using the date/time wildcards from the tcl clock command, as well as providing a variable to include the hostname It requires only the WebGUI to establish the configuration you desire It allows you to disable the processes for automated backup, without you having to remove the Application Service or losing any previously entered settings For the external shellscripts it automatically generates, the credentials are stored in encrypted form (using the master key) It allows you to no longer be required to make modifications on the linux command line to get your automated backups running after an RMA or restore operation It cleans up after itself, which means there are no extraneous shellscripts or status files lingering around after the scripts execute Enjoy! Thomas Schockaert Contributed by: Thomas Schockaert How to use this snippet: minimum version 11.4 Code : 67735 Tested this on version: 11.46.6KViews0likes51CommentsSaaS Federation iApp
Problem this snippet solves: f5.saas_idp.v.1.0.1rc1 The official release candidate iApp template has been posted to downloads.f5.com in the RELEASE_CANDIDATE directory of the iApp package. This release has the following changes: Added support for BIG-IP v12.1 Modified the 'SP Initiated?' field in the iApp to 'IdP Initiated?' and the values to 'No, SP only' and 'Yes, IdP and SP' to make this section more clear. f5.saas_idp.v.1.0.0rc1 This release candidate version of the iApp template, released on 4/20/16, provides improved functionality and additional options. The deployment guide has also been substantially updated. f5.saas_idp.v.0.9.0 This iApp allows you to configure F5 BIG-IP Access Policy Manager(APM) as SAML Identity Provider(IdP) to 11 commonly used SaaS applications: Office 365 Salesforce.com Workday Amazon Web Services(limited support) Concur Service-Now Jive Wombat Zendesk WebEx Google Apps How to use this snippet: For information on how to download, install, and use the iApp (and various other prerequisites), see the deployment guide for this configuration: http://f5.com/pdf/deployment-guides/saml-idp-saas-dg.pdf Code : https://downloads.f5.com/esd/product.jsp?sw=BIG-IP&pro=iApp_Templates646Views0likes2CommentsF5 iApp Automated Backup
Problem this snippet solves: This is now available on GitHub! Please look on GitHub for the latest version, and submit any bugs or questions as an "Issue" on GitHub: (Note: DevCentral admin update - Daniel's project appears abandoned so it's been forked and updated to the link below. @damnski on github added some SFTP code that has been merged in as well.) https://github.com/f5devcentral/f5-automated-backup-iapp Intro Building on the significant work of Thomas Schockaert (and several other DevCentralites) I enhanced many aspects I needed for my own purposes, updated many things I noticed requested on the forums, and added additional documentation and clarification. As you may see in several of my comments on the original posts, I iterated through several 2.2.x versions and am now releasing v3.0.0. Below is the breakdown! Also, I have done quite a bit of testing (mostly on v13.1.0.1 lately) and I doubt I've caught everything, especially with all of the changes. Please post any questions or issues in the comments. Cheers! Daniel Tavernier (tabernarious) Related posts: Git Repository for f5-automated-backup-iapp (https://github.com/tabernarious/f5-automated-backup-iapp) https://community.f5.com/t5/technical-articles/f5-automated-backups-the-right-way/ta-p/288454 https://community.f5.com/t5/crowdsrc/complete-f5-automated-backup-solution/ta-p/288701 https://community.f5.com/t5/crowdsrc/complete-f5-automated-backup-solution-2/ta-p/274252 https://community.f5.com/t5/technical-forum/automated-backup-solution/m-p/24551 https://community.f5.com/t5/crowdsrc/tkb-p/CrowdSRC v3.2.1 (20201210) Merged v3.1.11 and v3.2.0 for explicit SFTP support (separate from SCP). Tweaked the SCP and SFTP upload directory handling; detailed instructions are in the iApp. Tested on 13.1.3.4 and 14.1.3 v3.1.11 (20201210) Better handling of UCS passphrases, and notes about characters to avoid. I successfully tested this exact passphrase in the 13.1.3.4 CLI (surrounded with single quote) and GUI (as-is): `~!@#$%^*()aB1-_=+[{]}:./? I successfully tested this exact passphrase in 14.1.3 (square-braces and curly-braces would not work): `~!@#$%^*()aB1-_=+:./? Though there may be situations these could work, avoid these characters (separated by spaces): " ' & | ; < > \ [ ] { } , Moved changelog and notes from the template to CHANGELOG.md and README.md. Replaced all tabs (\t) with four spaces. v3.1.10 (20201209) Added SMB Version and SMB Security options to support v14+ and newer versions of Microsoft Windows and Windows Server. Tested SMB/CIFS on 13.1.3.4 and 14.1.3 against Windows Server 2019 using "2.0" and "ntlmsspi" v3.1.0: Removed "app-service none" from iCall objects. The iCall objects are now created as part of the Application Service (iApp) and are properly cleaned up if the iApp is redeployed or deleted. Reasonably tested on 11.5.4 HF2 (SMB worked fine using "mount -t cifs") and altered requires-bigip-version-min to match. Fixing error regarding "script did not successfully complete: (can't read "::destination_parameters__protocol_enable": no such variable" by encompassing most of the "implementation" in a block that first checks $::backup_schedule__frequency_select for "Disable". Added default value to "filename format". Changed UCS default value for $backup_file_name_extension to ".ucs" and added $fname_noext. Removed old SFTP sections and references (now handled through SCP/SFTP). Adjusted logging: added "sleep 1" to ensure proper logging; added $backup_directory to log message. Adjusted some help messages. New v3.0.0 features: Supports multiple instances! (Deploy multiple copies of the iApp to save backups to different places or perhaps to keep daily backups locally and send weekly backups to a network drive.) Fully ConfigSync compatible! (Encrypted values now in $script instead of local file.) Long passwords supported! (Using "-A" with openssl which reads/writes base64 encoded strings as a single line.) Added $script error checking for all remote backup types! (Using 'catch' to prevent tcl errors when $script aborts.) Backup files are cleaned up after any $script errors due to new error checking. Added logging! (Run logs sent to '/var/log/ltm' via logger command which is compatible with BIG-IP Remote Logging configuration (syslog). Run logs AND errors sent to '/var/tmp/scriptd.out'. Errors may include plain-text passwords which should not be in /var/log/ltm or syslog.) Added custom cipher option for SCP! (In case BIG-IP and the destination server are not cipher-compatible out of the box.) Added StrictHostKeyChecking=no option. (This is insecure and should only be used for testing--lots of warnings.) Combined SCP and SFTP because they are both using SCP to perform the remote copy. (Easier to maintain!) Original v1.x.x and v2.x.x features kept (copied from an original post): It allows you to choose between both UCS or SCF as backup-types. (whilst providing ample warnings about SCF not being a very good restore-option due to the incompleteness in some cases) It allows you to provide a passphrase for the UCS archives (the standard GUI also does this, so the iApp should too) It allows you to not include the private keys (same thing: standard GUI does it, so the iApp does it too) It allows you to set a Backup Schedule for every X minutes/hours/days/weeks/months or a custom selection of days in the week It allows you to set the exact time, minute of the hour, day of the week or day of the month when the backup should be performed (depending on the usefulness with regards to the schedule type) It allows you to transfer the backup files to external devices using 4 different protocols, next to providing local storage on the device itself SCP (username/private key without password) SFTP (username/private key without password) FTP (username/password) SMB (now using TMOS v12.x.x compatible 'mount -t cifs', with username/password) Local Storage (/var/local/ucs or /var/local/scf) It stores all passwords and private keys in a secure fashion: encrypted by the master key of the unit (f5mku), rendering it safe to store the backups, including the credentials off-box It has a configurable automatic pruning function for the Local Storage option, so the disk doesn't fill up (i.e. keep last X backup files) It allows you to configure the filename using the date/time wildcards from the tcl [clock] command, as well as providing a variable to include the hostname It requires only the WebGUI to establish the configuration you desire It allows you to disable the processes for automated backup, without you having to remove the Application Service or losing any previously entered settings For the external shellscripts it automatically generates, the credentials are stored in encrypted form (using the master key) It allows you to no longer be required to make modifications on the linux command line to get your automated backups running after an RMA or restore operation It cleans up after itself, which means there are no extraneous shellscripts or status files lingering around after the scripts execute How to use this snippet: Find and download the latest iApp template on GitHub (e.g "f5.automated_backup.v3.2.1.tmpl.tcl"). Import the text file as an iApp Template in the BIG-IP GUI. Create an Application Service using the imported Template. Answer the questions (paying close attention to the help sections). Check /var/tmp/scriptd.out for general logs and errors. Tested this on version: 16.021KViews5likes101CommentsAWS Advanced HA iApp
Problem this snippet solves: SUMMARY For customers who want to deploy Public Internet-facing services on a traditional High Availability pair but also leverage the benefits of AWS’s Availability Zones, we introduce the AWS Advanced HA iApp. The AWS Advanced HA iApp helps facilitate the HA Across AZs deployment, an additional deployment option to those discussed in: F5 in AWS Part 1 - AWS Networking Basics F5 in AWS Part 2 - Running BIG-IP in an EC2 Virtual Private Cloud F5 in AWS Part 3 - Advanced Topologies and More on Highly Available Services As well as enable traditional HA pairs to perform basic route management of AWS route tables (whether in “Single AZ” or the “Across AZ” deployments introduced above). By having the Active BIG-IP take ownership of your client’s or application’s “default” or specific routes, this enables: Virtual Servers to avoid using SNAT* Manage Access Traffic (ex. point clients/servers to BIG-IP APM VPN for specific on-prem networks)* Facilitate various outbound proxy use cases (NAT, URI filtering, etc) For more information, see the Deployment Guide Minimum required BIG-IP version: 12.1.0 HF2. Supported BIG-IP versions: 12.1.0 HF2. Various Cloud Formation Templates that automate the installation of this solution are available on github. They are listed under the naming format "full-stack-across-az-cluster-*" We recommend you first start with "full-stack-" examples to get a fully functioning reference deployment and the "existing-*" ones once you're comfortable with the general requirements (security groups, route tables, etc). RELEASES v1.0.0 iApp template to configure HA Across Availability Zones in AWS by managing EIP mappings. v1.0.1 iApp template to configure HA Across Availability Zones in AWS by managing EIP and route mappings. v1.0.1rc1 This version 1.0.1rc1 of the iApp template is available at downloads.f5.com. This official release candidate version contains the same functionality as the previous version (v1.0.1 on DevCentral), but the presentation has been updated. There is also a new F5 deployment guide to accompany the iApp template. Go to https://downloads.f5.com/esd/index.jsp.2. Click Find a Download. Click iApp Templates. Accept the EULA, and then download the iapps zip file to a location accessible from your BIG-IP system. Extract (unzip) the f5.aws_advanced_ha.v1.0.1rc1.tmpl file, found in the **_RELEASE CANDIDATE_** directory of the zip file. v1.1.0rc1 This version 1.1.0rc1 template is available for download on this page. F5 has released version 1.1.0rc1 of the iApp. This official release candidate version contains the same functionality as the previous version (v1.0.1rc1 on DevCentral), with further checks and support added for AWS EIP ownership. In previous versions, EIP ownership was indicated by which device owns the default floating traffic-group-1. As there was only one floating traffic group, this affected the global Active/Standby status of the devices. This iApp has been updated to validate EIP ownership after an active-active state scenario and ensures the current EIP owner is Active for traffic-group-1 (the traffic-group tied to the EIP mappings). CAVEATS Both BIG-IP devices will now report active for their global status, as each device will now own a device specific traffic group. The active status of the default floating traffic-group-1 will indicate which BIG-IP device is actively serving EIP production traffic. The BIG-IP hostname should not be changed during an active deployment of this iApp. This solution only supports the 3 traffic groups as described above. INSTALLATION If you are using a previous version of this iApp and need to update the application service with this updated iApp, there are two prerequisites before you run this iApp: Remove from the file /config/failover/active this line "python /config/failover/aws_advanced_failover.py” Delete the file /config/failover/aws_advanced_failover.py entirely Reparent the application service to use the new version of iApp template This new version will modify the following files in /config/failover on first configuration run tgactive (modify) tgstandby (modify) and install the following aws_af_tgactive aws_af_tgstandby aws_advanced_failover.py aws_advanced_failover.dat On subsequent reconfiguration, it updates the above 4 files listed. v1.2.0rc1 This version 1.2.0rc1 template is available for download on this page. F5 has released version 1.2.0rc1 of the iApp. This official release candidate version contains the same functionality as the previous version (v1.0.1rc1 on DevCentral, or v1.1.0rc1), with further checks and support added for AWS EIP ownership. In previous versions, EIP ownership was indicated by which device owns the default floating traffic-group-1. As there was only one floating traffic group, this affected the global Active/Standby status of the devices. This iApp has been updated to validate EIP ownership after an active-active state scenario and ensures the current EIP owner is Active. CAVEATS Noted caveats from v1.1.0rc1 removed. INSTALLATION If you are using a previous version of this iApp and need to update the application service with this updated iApp, there are a few prerequisites before you run this iApp: Remove from the file /config/failover/active this line "python /config/failover/aws_advanced_failover.py” Delete the file /config/failover/aws_advanced_failover.py entirely Delete the file /config/failover/aws_af_tgactive (if previous version is v1.1.0rc1) Delete the file /config/failover/aws_af_tgstandby (if previous version is v1.1.0rc1) Reparent the application service to use the new version of iApp template This new version will modify the following files in /config/failover on first configuration run tgactive (modify) tgstandby (modify) tgrefresh (modify) and install the following aws_advanced_failover.py aws_advanced_failover.dat On subsequent reconfiguration, it updates the above 2 files listed. Code : 708281.2KViews0likes3CommentsUDP TCP Packet Duplication
Problem this snippet solves: This iApp provides full configuration of UDP/TCP packet duplication. It is commonly used to duplicate Syslog, SNMP Traps, Netflow, and Sflow data streams to multiple vendor solutions or customers. It also provides fault tolerance capabilities within each duplicated destination. By pointing Network devices, Appliances, and Servers to a VIP distributing network management traffic modifying distribution of streams can be done in one centralized location. UDP packets retain the original source address when sending to the destination locations. Notes: Prior to 11.5 you must add an IPv6 address to any interface to allow for HSL traffic to be sent to the distribution virtual fdf5::1/64 fdf5::2/64 for an HA pair would do it.* TCP traffic does not maintain original source Internal F5 Resources can demo this solution within the UDF environment using the blueprint named "Traffic Duplication Demo" Contributed by: Ken Bocchino 20200807 - Updated to v2.2 How to use this snippet:7.2KViews6likes38CommentsMySQL Proxy iApp
Problem this snippet solves: It is likely the MySQL proxy iRule will not work unless you are running BIG-IP 11.1 and MySQL 5.1, which were the versions tested against. Please contact your F5 salesperson to request this functionality be added natively to BIG-IP ¶ iApp to implement Layer 7 full proxy for MySQL servers. This proxy alleviates the need for a developer to modify his/her application to use multiple database handles when scaling from a standalone MySQL server to a Master/Slave cluster. Read-only queries are sent to the slave pool while all others are sent to the master pool. This iApp uses iRule functionality that is only available on BIG-IP versions 11.1.0 and later. The latest version of this iApp is named mysql_proxy.2011-12-02 MySQL Proxy iRule Contributed by: Ryan Corder1.2KViews1like5CommentsCVE-2021 Checker iApp
Problem this snippet solves: Overview This iApp shows you at a glance the vulnerability status of your BIG-IP against the March 2021 CVEs. This is based on the software version mainly and the modules provisioned, appliance mode etc, it does not look at your configuration in detail so it is only to be used as a guide. For instance, it does not check whether you are actually using APM, or SNAT, or HTTP/2. There are two reports - the at-a-glance report on the Critical CVEs, and a more detailed HTML report created in the /var/tmp directory of the device which shows all of the BIG-IP CVEs and performs more detailed checks. Summary Report Detailed Report How to use this snippet: Download the file and extract to a local directory Install the template as normal: login to the BIG-IP TMUI and go to iApps>Templates>Templates. Click on Import ( on the right hand side) Select the cve-checker-2021.tmpl file and hit Upload To see the report, create an app using this template Go to iApps>Application Services>Applications Click on Create ( on the right hand side ) From Template, select cve-checker-2021 View summary report in this window Add a name for the application and Hit Finished Retrieve report from /var/tmp To refresh the report, go to Reconfigure and hit Finished again If you find any bugs or issues with this then feel free to PM me here This code has been developed and tested in a lab so you use it at your own risk. If you have used it and found it to be accurate, or have suggestions for further development then please PM me Tested this on version: 13.11.4KViews2likes7CommentsH.323 ALG iApps
Problem this snippet solves: This iApps creates H.323 ALG configuration including virtual servers, iRules, LSN pools, etc. This H.323 ALG is implemented using iRules (Tcl). It makes use of new CGNAT ALG Toolkit iRules primitive available in BIG-IP 14.1. The configuration consists of virtual server which intercept H.225 RAS (Registration, Admission, and Status) traffic. The ALG will extract information from H.225 traffic and start listeners for H.225 CS (call signaling) as necessary. The ALG will follow H.245 connection created by H.225 CS if there is any. It will also create flows for media connections based on negotiation happened in H.245 protocol level. The ALG can also intercept H.225 CS call which happens without H.225 RAS. Note that this iApps only support public vlan that is in route-domain 0 (default). How to use this snippet: This H.323 ALG iApps supports 2 main use cases 1. NAT44 2. 464XLAT Note that vlan, route, IP address configuration are not included in the iApps. They may be configured prior to create the application. For vlans, some NAT mode may requires specific cmp-hash mode, for example, PBA and DNAT require cmp-hash as src-ip on private side and cmp-hash as dst-ip on public side. Configuration for NAT44 and 464XLAT use cases are separated. Configuration objects (including virtual servers, LSN pool, etc) will be created separately and are not shared. Both options can be enabled in the same application. * * * To create application for NAT44 use case (see image below), select "yes" in "Enabled H.323 ALG for NAT44" section. Then enter information for private and public side. For private side, add virtual server to intercept H.225 RAS and H.225 CS (enter vlan, route-domain, port). Enter "allowed source" subnet information. To support hairpin call, select "yes" to "Create wildcard virtual..." option if there is no existing wildcard virtual that matches hairpin traffic. For public side, select public vlan, enter LSN pool member addresses and configure translation mode. * * * To create application for 464XLAT use case (see image below), select "yes" in "Enabled H.323 ALG for 464XLAT" section. Follow the same instruction as NAT44 use case. However, use appropriate IPv6 address as needed. The 464XLAT use case also require NAT64 prefix as BIG-IP acts as a PLAT. * * * In addition to configuration sections for NAT44 and 464XLAT use case. There are sections for Advance, Logging and Debug options (see image below). For "Advance Options" section: * Enforce no H.245 Tunnelling : select yes if you want ALG to try to prevent H.245 tunnelling in H.225 CS * Enforce no FastStart : select yes if you want ALG to try to prevent FastStart to be used * Choose action for ALG to take when iRules receives message it could not decode For "Logging Options" section: * Set LSN log destination : choose either to write log to local syslog or none For "Debug Options" section, enable debug log on categories of interest. Note that "per" debug log may produce very detail log information. Debug log option may only be enabled at no load. This iApps is created on "Wed May 09 04:45:14 GMT 2018" Tested this on version: No Version Found638Views0likes0CommentsAPM VPN Bandwidth Controller iApp
Problem this snippet solves: Overview This iApp will create a set of virtual servers to apply a Bandwidth Controller policy to VPN tunnel traffic. Example iperf without the iApp: $ iperf -c 10.20.20.3 ------------------------------------------------------------ Client connecting to 10.20.20.3, TCP port 5001 TCP window size: 64.0 KByte (default) ------------------------------------------------------------ [ 3] local 10.20.20.131 port 5957 connected with 10.20.20.3 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 184 MBytes 154 Mbits/sec iperf with 10Mbps dynamic policy $ iperf -c 10.20.20.3 ------------------------------------------------------------ Client connecting to 10.20.20.3, TCP port 5001 TCP window size: 64.0 KByte (default) ------------------------------------------------------------ [ 3] local 10.20.20.131 port 6066 connected with 10.20.20.3 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.2 sec 12.1 MBytes 9.98 Mbits/sec iperf with 1Gbps dynamic policy $ iperf -c 10.20.20.3 ------------------------------------------------------------ Client connecting to 10.20.20.3, TCP port 5001 TCP window size: 64.0 KByte (default) ------------------------------------------------------------ [ 3] local 10.20.20.131 port 6569 connected with 10.20.20.3 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 190 MBytes 159 Mbits/sec Function This creates a set of virtual servers listening on the VPN tunnel with an iRule assigned which applies the BWC policy to both upload and download traffic. You can change your BWC rate as you require, it will be applied to new flows ie users don't have to reconnect. This has been tested that it deploys and works on v13 but I have not tested this in a production environment, therefore you should test its usage prior to implementation in a production environment. If you have successfully tested it then please PM with details and/or suggestions. How to use this snippet: Usage Instructions This assumes that you already have a VPN configured Create a Bandwidth Controller Policy with the overall bandwidth for the WHOLE VPN, and enable Dynamic if you want to specify the bandwidth for each user. In the example below, the Maximum Rate of 20Mbps is applied to the whole VPN and 10Mbps is applied to each flow. Load the iApp template at iApps>Templates and Import Deploy a new iApp service at iApps>Application Services>Applications and select the apm_bwc_iapp template Select the VPN tunnel and the Default BWC policy Select any SNAT requirements If you want to apply different rates to different traffic then add entries in the Protocol-specific Bandwidth Controller table. If you don't want to add these then click the X to remove the default entry. Hit Finished Objects created: Code : cli admin-partitions { update-partition Common } sys application template /Common/apm_bwc_iapp { actions { definition { html-help { } implementation { set app_dir [tmsh::pwd] set app_name $tmsh::app_name # https://support.f5.com/csp/article/K54955814 set rule_tcp {when CLIENT_ACCEPTED { BWC::policy attach <%=$bwc_policy%> "[IP::remote_addr]:[TCP::remote_port]" } when SERVER_CONNECTED { BWC::policy attach <%=$bwc_policy%> "[IP::remote_addr]:[TCP::remote_port]" } } set rule_udp {when CLIENT_ACCEPTED { BWC::policy attach <%=$bwc_policy%> "[IP::remote_addr]:[UDP::remote_port]" } when SERVER_CONNECTED { BWC::policy attach <%=$bwc_policy%> "[IP::remote_addr]:[UDP::remote_port]" } } if { $::main__use_snat == "Automap" } { set snat "source-address-translation \{ type automap \} " } elseif { $::main__use_snat == "SNAT Pool" } { set snat "source-address-translation \{ type snat pool $::main__snatpool \} " } else { set snat "" } # Create default iRule tmsh::create ltm rule rule_bwc_${app_name}_udp_default [ tmsh::expand_macro $rule_udp -vars "bwc_policy \"$::main__bwc_policy\"" ] tmsh::create ltm rule rule_bwc_${app_name}_tcp_default [ tmsh::expand_macro $rule_tcp -vars "bwc_policy \"$::main__bwc_policy\"" ] # Create default VS tmsh::create ltm virtual vs_bwc_${app_name}_udp_default ip-protocol udp vlans-enabled vlans replace-all-with \{ $::main__tunnel \} destination 0.0.0.0:any mask any $snat profiles replace-all-with \{ udp \} rules \{ rule_bwc_${app_name}_udp_default \} source 0.0.0.0/0 translate-address disabled translate-port disabled tmsh::create ltm virtual vs_bwc_${app_name}_tcp_default ip-protocol tcp vlans-enabled vlans replace-all-with \{ $::main__tunnel \} destination 0.0.0.0:any mask any $snat profiles replace-all-with \{ tcp \} rules \{ rule_bwc_${app_name}_tcp_default \} source 0.0.0.0/0 translate-address disabled translate-port disabled # Create custom ports and iRules foreach {row} $::main__entries { array set cols [lindex $row 0] # protocol, port and bwc_policy set rulename "rule_bwc_${app_name}_$cols(protocol)_$cols(port)" set vsname "vs_bwc_${app_name}_$cols(protocol)_$cols(port)" if { $cols(protocol) == "tcp" } { tmsh::create ltm rule $rulename [tmsh::expand_macro $rule_tcp -vars "bwc_policy \"$cols(bwc_policy)\"" ] tmsh::create ltm virtual $vsname ip-protocol tcp vlans-enabled vlans replace-all-with \{ $::main__tunnel \} destination 0.0.0.0:$cols(port) mask any $snat profiles replace-all-with \{ $cols(protocol) \} rules \{ $rulename \} source 0.0.0.0/0 translate-address disabled translate-port disabled } else { tmsh::create ltm rule $rulename [tmsh::expand_macro $rule_udp -vars "bwc_policy \"$cols(bwc_policy)\"" ] tmsh::create ltm virtual $vsname ip-protocol udp vlans-enabled vlans replace-all-with \{ $::main__tunnel \} destination 0.0.0.0:$cols(port) mask any $snat profiles replace-all-with \{ $cols(protocol) \} rules \{ $rulename \} source 0.0.0.0/0 translate-address disabled translate-port disabled } } } macro { } presentation { section main { # The entry below creates a large text box that must be filled out with a valid IP Address # For details of APL, look at the iApps developers guide: # https://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/bigip-iapps-developer-11-4-0.html message intro "This iApp will create a forwarding virtual server on the specified VPN tunnel which intercepts the traffic and assigns a BWC policy" choice tunnel display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items net tunnel]" } choice bwc_policy display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items -norecursive net bwc policy]" } choice use_snat display "large" default "None" { "None" => "None", "Automap" => "Automap", "SNAT Pool" => "SNAT Pool" } optional (use_snat == "SNAT Pool") { choice snatpool display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items ltm snatpool]" } } table entries { choice protocol display "large" default "tcp" { "tcp" => "tcp", "udp" => "udp" } string port display "large" required validator "PortNumber" default "443" choice bwc_policy display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items -norecursive net bwc policy]" } } } text { # Entities below set the text for the questions and section names, etc. Make them simple and relevant. main "Main" main.intro "Usage" main.tunnel "VPN Tunnel" main.bwc_policy "Default BWC Policy" main.use_snat "Source Address Translation" main.snatpool "SNAT Pool" main.entries "Protocol-specific Bandwidth Controller" main.entries.protocol "Protocol" main.entries.port "Port" main.entries.bwc_policy "BWC Policy" } } role-acl none run-as none } } description "iApp to create an outgoing VS to apply a BWC policy to VPN user traffic v2" ignore-verification false requires-bigip-version-max none requires-bigip-version-min none requires-modules { apm } signing-key none tmpl-checksum none tmpl-signature none } Tested this on version: 13.0920Views2likes0CommentsSSL VPN Tunnel and Office 365
Problem this snippet solves: This implements Regan Anderson's script from https://devcentral.f5.com/s/articles/SSL-VPN-Split-Tunneling-and-Office-365 as an iApp for simple deployment. This will install and configure the script and also create an iCall which runs every day at midnight. Also available at https://github.com/pwhitef5/apm-vpn-optimisation/blob/master/o365_iapp.tmpl How to use this snippet: Add the iApp via iApps>Templates Create a new application via iApps>Application Services>Applications, use the o365_optimisation iApp template Select your requirements ( remember to delete additional URLs and IPs if not used ) and hit finished Code : cli admin-partitions { update-partition Common } sys application template /Common/o365_optimisation { actions { definition { html-help { Office 365 Optimisation iCall Version 1 To see this in a wider window, hit the Launch button above For help on the various options, go to https://github.com/f5regan/o365-apm-split-tunnel This script fetches Office 365 URLs and IPs (IPv4 and/or IPv6) from Microsoft's Office 365 IP Address and URL web service, dynamically updates Network Access List "Exclude" properties for one or more Network Access Lists, and applies changes to the affected Access Policies. If the script is running on an HA pair of BIG-IPs then the script will also initiate a ConfigSync to push the updated configuration from the active BIG-IP to the standby BIG-IP. Script Requirements TMOS 12.1.0 or higher BIG-IP must be capable of resolving internet DNS names (ex. via DNS Lookup Server configuration) BIG-IP must be able to reach endpoints.office.com via TCP 443 (via Management or TMM interface) Administrative rights on the BIG-IP(s) Bash shell access on the BIG-IP(s) Things to Note This software is supplied "AS IS" without any warranties or support. This script does not enable “split tunneling” or make any other modifications, other than those mentioned, to the Network Access List(s) that may be required to enable the desired functionality. For guidance relating to Network Access List / Split Tunnelling configuration refer to the BIG-IP APM Knowledge Center. Some split tunneling guidance: Allow Local DNS Servers should be enabled to allow client access to Office 365 when VPN is disconnected DNS Exclude Address Space is not supported on macOS IPV6 Exclude Address Space doesn't currently work on macOS This script should not be used with BIG-IP Edge Client's Always Connected Mode: if Stonewall is configured to block traffic, then the excluded resources are not reachable (this is by design). This script tracks the version of the Office 365 service instance and will only update the exclusion lists if a newer version is detected. If modifications to the script's operating parameters (ex. Network Access Lists, O365 Service Areas, Additional URLs/IPs to Exclude) are made, they will NOT take effect until the script detects a new service instance version. To force the script to run with the updated parameters earlier, remove the o365_version.txt file from the script's working directory OR temporarily set force_o365_record_refresh = 1, then manually execute the script (python /shared/o365/apm_o365_update.py). This script overwrites the contents of the following fields in the defined Network Access Lists when an update is detected: If "use_url" is set to 1: DNS Exclude Address Space If "use_ipv4" is set to 1: IPV4 Exclude Address Space If "use_ipv6" is set to 1: IPV6 Exclude Address Space The aforementioned fields / properties should not be managed via TMSH or the GUI after implementing this script as the script will overwrite any manual changes to these fields when it detects the next Office 365 service instance update. To add non-Office 365 URLs/IPs to or remove any URL/IP (Office 365 or otherwise) from the "Exclude Address Space" properties of the Network Access Lists, use the noimport_* and additional_* fields in the script (see User Options in the Appendix for usage details) . Be sure to apply these changes to both units of an HA pair! Usage of DNS Address Exclusions requires the installation of the DNS Relay Proxy service on the VPN client K9694: Overview of the Windows DNS Relay Proxy service K49720803: BIG-IP Edge Client operations guide | Chapter 3: Common approaches to configuring VPN While the endpoints retrieved by this script handle the vast majority of Office 365 traffic, it is possible some traffic related to Office 365 is not summarized in the endpoint lists and will still go through the VPN. HA: This script must be implemented on both members of an HA pair HA: Updates made to the Python script are NOT synchronized between peers - updates must be made manually to each instance of the Python script HA: Exclusion list updates will only take place on the Active member - changes are synced from the Active to Standby by the script This script relies on iCall to periodically run. See What is iCall? on DevCentral for an overview of iCall. iCall scripts and periodic handlers are documented in greater detail in the F5 TMSH Reference. } implementation { set app_dir [tmsh::pwd] set app_name $tmsh::app_name set script {#!/usr/bin/env python # -*- coding: utf-8 -*- # O365 URL/IP update automation for BIG-IP # Version: 1.1 # Last Modified: 01 April 2020 # Original author: Makoto Omura, F5 Networks Japan G.K. # # Modified for APM Network Access "Exclude Address Space" by Regan Anderson, F5 Networks # Modified for iCall by Peter White, F5 Networks # # This Sample Software provided by the author is for illustrative # purposes only which provides customers with programming information # regarding the products. This software is supplied "AS IS" without any # warranties and support. # # The author assumes no responsibility or liability for the use of the # software, conveys no license or title under any patent, copyright, or # mask work right to the product. # # The author reserves the right to make changes in the software without # notification. The author also make no representation or warranty that # such application will be suitable for the specified use without # further testing or modification. #----------------------------------------------------------------------- import httplib import urllib import uuid import os import re import json import commands import datetime import sys #----------------------------------------------------------------------- # User Options - Configure as desired #----------------------------------------------------------------------- # Access Profile Name(s) - ex. SINGLE ["AP1"] OR MULTIPLE ["AP1", "AP2", "AP3"] access_profiles = [} set accessProfiles {} set naList {} foreach {row} $::main__access_profile { array set cols [lindex $row 0] lappend accessProfiles "\"$cols(profile)\" " lappend naLists "\"$cols(nalist)\" " } append script [ join $accessProfiles , ] append script {] # Network Access List Name(s) - ex. SINGLE ["NAL1"] OR MULTIPLE ["NAL1", "NAL2", "NAL3"] na_lists = [} append script [ join $naLists , ] append script {] # Microsoft Web Service Customer endpoints (ENABLE ONLY ONE ENDPOINT) # These are the set of URLs defined by customer endpoints as described here: https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges customer_endpoint = } append script "\"$::main__endpoint\"" append script { # O365 "SeviceArea" (O365 endpoints) to consume, as described here: https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges care_exchange = } append script $config__include_exchange append script { # "Exchange Online": 0=do not care, 1=care care_sharepoint = } append script $config__include_sharepoint append script { # "SharePoint Online and OneDrive for Business": 0=do not care, 1=care care_skype = } append script $config__include_skype append script { # "Skype for Business Online and Microsoft Teams": 0=do not care, 1=care care_common = } append script $config__include_common append script { # "Microsoft 365 Common and Office Online": 0=do not care, 1=care # O365 Record types to download & update use_url = } append script $config__use_url append script { # DNS/URL exclusions: 0=do not use, 1=use use_ipv4 = } append script $config__use_ipv4 append script { # IPv4 exclusions: 0=do not use, 1=use use_ipv6 = } append script $config__use_ipv6 append script { # IPv6 exclusions: 0=do not use, 1=use # O365 Categories to download & update o365_categories = } append script $config__o365_categories append script { # 0=Optimize only, 1= Optimize & Allow, 2 = Optimize, Allow, and Default # O365 Endpoints to import - O365 required endpoints or all endpoints # WARNING: "import all" includes non-O365 URLs that one may not want to bypass (ex. www.youtube.com) only_required = } append script $config__only_required append script { # 0=import all, 1=O365 required only # Don't import these O365 URLs (URL must be exact or ends_with match to URL as it exists in JSON record - pattern matching not supported) # Provide URLs in list format - ex. [".facebook.com", "*.itunes.apple.com", "bit.ly"] #noimport_urls = [] noimport_urls = [".symcd.com",".symcb.com",".entrust.net",".digicert.com",".identrust.com",".verisign.net",".globalsign.net",".globalsign.com",".geotrust.com",".omniroot.com",".letsencrypt.org",".public-trust.com","platform.linkedin.com"] # Don't import these O365 IPs (IP must be exact match to IP as it exists in JSON record - IP/CIDR mask cannot be modified) # Provide IPs (IPv4 and IPv6) in list format - ex. ["191.234.140.0/22", "2620:1ec:a92::152/128"] noimport_ips = [] # Non-O365 URLs to add to DNS Exclude List # Provide URLs in list format - ex. ["m.facebook.com", "*.itunes.apple.com", "bit.ly"] additional_urls = [} set additional_urls {} foreach {row} $::config__additional_urls { array set cols [lindex $row 0] lappend additional_urls "\"$cols(url)\"" } append script [ join $additional_urls , ] append script {] # Non-O365 IPs to add to IPV4 Exclude List # Provide IPs in list format - ex. ["191.234.140.0/22", "131.253.33.215/32"] additional_ipv4 = [} set additional_ips {} foreach {row} $::config__additional_ips { array set cols [lindex $row 0] lappend additional_ips "\"$cols(ip)\"" } append script [ join $additional_ips , ] append script {] # Non-O365 IPs to add to IPV6 Exclude List # Provide IPs in list format - ex. ["2603:1096:400::/40", "2620:1ec:a92::152/128"] additional_ipv6 = [] # Action if O365 endpoint list is not updated force_o365_record_refresh = 0 # 0=do not update, 1=update (for test/debug purpose) # BIG-IP HA Configuration device_group_name = "} append script $config__dg append script {" # Name of Sync-Failover Device Group. Required for HA paired BIG-IP. ha_config = } if { [llength [tmsh::get_config cm device]] > 1 } { append script "1" } else { append script "0" } append script { # 0=stand alone, 1=HA paired # Log configuration log_level = } append script $main__debug append script { # 0=none, 1=normal, 2=verbose #----------------------------------------------------------------------- # System Options - Modify only when necessary #----------------------------------------------------------------------- # Working directory, file name for guid & version management work_directory = "/shared/o365/" file_name_guid = "/shared/o365/guid.txt" file_ms_o365_version = "/shared/o365/o365_version.txt" log_dest_file = "/var/log/o365_update" # Microsoft Web Service URLs url_ms_o365_endpoints = "endpoints.office.com" url_ms_o365_version = "endpoints.office.com" uri_ms_o365_version = "/version?ClientRequestId=" #----------------------------------------------------------------------- # Implementation - Please do not modify #----------------------------------------------------------------------- list_urls_to_exclude = [] list_ipv4_to_exclude = [] list_ipv6_to_exclude = [] def log(lev, msg): if log_level >= lev: log_string = "{0:%Y-%m-%d %H:%M:%S}".format(datetime.datetime.now()) + " " + msg + "\n" f = open(log_dest_file, "a") f.write(log_string) f.flush() f.close() return def main(): # ----------------------------------------------------------------------- # Check if this BIG-IP is ACTIVE for the traffic group (= traffic_group_name) # ----------------------------------------------------------------------- result = commands.getoutput("tmsh show /cm failover-status field-fmt") if ("status ACTIVE" in result) or (ha_config == 0): log(1, "This BIG-IP is standalone or HA ACTIVE. Initiating O365 update.") else: log(1, "This BIG-IP is HA STANDBY. Aborting O365 update.") sys.exit(0) # ----------------------------------------------------------------------- # GUID management # ----------------------------------------------------------------------- # Create guid file if not existent if not os.path.isdir(work_directory): os.mkdir(work_directory) log(1, "Created work directory " + work_directory + " because it did not exist.") if not os.path.exists(file_name_guid): f = open(file_name_guid, "w") f.write("\n") f.flush() f.close() log(1, "Created GUID file " + file_name_guid + " because it did not exist.") # Read guid from file and validate. Create one if not existent f = open(file_name_guid, "r") f_content = f.readline() f.close() if re.match('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', f_content): guid = f_content log(2, "Valid GUID is read from local file " + file_name_guid + ".") else: guid = str(uuid.uuid4()) f = open(file_name_guid, "w") f.write(guid) f.flush() f.close() log(1, "Generated a new GUID, and saved it to " + file_name_guid + ".") # ----------------------------------------------------------------------- # O365 endpoints list version check # ----------------------------------------------------------------------- # Read version of previously received record if os.path.isfile(file_ms_o365_version): f = open(file_ms_o365_version, "r") f_content = f.readline() f.close() # Check if the VERSION record format is valid if re.match('[0-9]{10}', f_content): ms_o365_version_previous = f_content log(2, "Valid previous VERSION found in " + file_ms_o365_version + ".") else: ms_o365_version_previous = "1970010200" f = open(file_ms_o365_version, "w") f.write(ms_o365_version_previous) f.flush() f.close() log(1, "Valid previous VERSION was not found. Wrote dummy value in " + file_ms_o365_version + ".") else: ms_o365_version_previous = "1970010200" f = open(file_ms_o365_version, "w") f.write(ms_o365_version_previous) f.flush() f.close() log(1, "Valid previous VERSION was not found. Wrote dummy value in " + file_ms_o365_version + ".") # ----------------------------------------------------------------------- # O365 endpoints list VERSION check # ----------------------------------------------------------------------- request_string = uri_ms_o365_version + guid conn = httplib.HTTPSConnection(url_ms_o365_version) conn.request('GET', request_string) res = conn.getresponse() if not res.status == 200: # MS O365 version request failed log(1, "VERSION request to MS web service failed. Assuming VERSIONs did not match, and proceed.") dict_o365_version = {} else: # MS O365 version request succeeded log(2, "VERSION request to MS web service was successful.") dict_o365_version = json.loads(res.read()) ms_o365_version_latest = "" for record in dict_o365_version: if record.has_key('instance'): if record["instance"] == customer_endpoint and record.has_key("latest"): latest = record["latest"] if re.match('[0-9]{10}', latest): ms_o365_version_latest = latest f = open(file_ms_o365_version, "w") f.write(ms_o365_version_latest) f.flush() f.close() log(2, "Previous VERSION is " + ms_o365_version_previous) log(2, "Latest VERSION is " + ms_o365_version_latest) if ms_o365_version_latest == ms_o365_version_previous and force_o365_record_refresh == 0: log(1, "You already have the latest MS O365 URL/IP Address list: " + ms_o365_version_latest + ". Aborting operation.") sys.exit(0) # ----------------------------------------------------------------------- # Request O365 endpoints list & put it in dictionary # ----------------------------------------------------------------------- request_string = "/endpoints/" + customer_endpoint + "?ClientRequestId=" + guid conn = httplib.HTTPSConnection(url_ms_o365_endpoints) conn.request('GET', request_string) res = conn.getresponse() if not res.status == 200: log(1, "ENDPOINTS request to MS web service failed. Aborting operation.") sys.exit(0) else: log(2, "ENDPOINTS request to MS web service was successful.") dict_o365_all = json.loads(res.read()) # Process for each record(id) of the endpoint JSON data for dict_o365_record in dict_o365_all: service_area = str(dict_o365_record['serviceArea']) category = str(dict_o365_record['category']) if (o365_categories == 0 and category == "Optimize") \ or (o365_categories == 1 and (category == "Optimize" or category == "Allow")) \ or (o365_categories == 2): if (only_required == 0) or (only_required and str(dict_o365_record['required']) == "True"): if (care_common and service_area == "Common") \ or (care_exchange and service_area == "Exchange") \ or (care_sharepoint and service_area == "SharePoint") \ or (care_skype and service_area == "Skype"): if use_url: # Append "urls" if existent in each record if dict_o365_record.has_key('urls'): list_urls = list(dict_o365_record['urls']) for url in list_urls: list_urls_to_exclude.append(url) # Append "allowUrls" if existent in each record if dict_o365_record.has_key('allowUrls'): list_allow_urls = list(dict_o365_record['allowUrls']) for url in list_allow_urls: list_urls_to_exclude.append(url) # Append "defaultUrls" if existent in each record if dict_o365_record.has_key('defaultUrls'): list_default_urls = dict_o365_record['defaultUrls'] for url in list_default_urls: list_urls_to_exclude.append(url) if use_ipv4 or use_ipv6: # Append "ips" if existent in each record if dict_o365_record.has_key('ips'): list_ips = list(dict_o365_record['ips']) for ip in list_ips: if re.match('^.+:', ip): list_ipv6_to_exclude.append(ip) else: list_ipv4_to_exclude.append(ip) log(1, "Number of unique ENDPOINTS to import...") # Add administratively defined URLs/IPs and (Re)process to remove duplicates and excluded values if use_url: # Combine lists and remove duplicate URLs urls_undup = list(set(list_urls_to_exclude + additional_urls)) ## Remove set of excluded URLs from the list of collected URLs for x_url in noimport_urls: urls_undup = [x for x in urls_undup if not x.endswith(x_url)] log(1, "URL: " + str(len(urls_undup))) if use_ipv4: # Combine lists and remove duplicate IPv4 addresses ipv4_undup = list(set(list_ipv4_to_exclude + additional_ipv4)) ## Remove set of excluded IPv4 addresses from the list of collected IPv4 addresses for x_ip in noimport_ips: ipv4_undup = [x for x in ipv4_undup if not x.endswith(x_ip)] log(1, "IPv4 host/net: " + str(len(ipv4_undup))) if use_ipv6: # Combine lists and duplicate IPv6 addresses ipv6_undup = list(set(list_ipv6_to_exclude + additional_ipv6)) ## Remove set of excluded IPv6 addresses from the list of collected IPv6 addresses for x_ip in noimport_ips: ipv6_undup = [x for x in ipv6_undup if not x.endswith(x_ip)] log(1, "IPv6 host/net: " + str(len(ipv6_undup))) # ----------------------------------------------------------------------- # URLs, IPv4 & IPv6 addresses formatted for TMSH # ----------------------------------------------------------------------- if use_url: # Initialize the URL string url_exclude_list = "" # Write URLs to string for url in urls_undup: url_exclude_list = url_exclude_list + " " + url.lower() if use_ipv4: # Initialize the IPv4 string ipv4_exclude_list = "" # Write IPv4 addresses to string for ip4 in (list(sorted(ipv4_undup))): ipv4_exclude_list = ipv4_exclude_list + "{subnet " + ip4 + " } " if use_ipv6: # Initialize the IPv6 string ipv6_exclude_list = "" # Write IPv6 addresses to string for ip6 in (list(sorted(ipv6_undup))): ipv6_exclude_list = ipv6_exclude_list + "{subnet " + ip6 + " } " # ----------------------------------------------------------------------- # Load URL and/or IPv4 and/or IPv6 lists into Network Access resource # ----------------------------------------------------------------------- if use_url: for na in na_lists: result = commands.getoutput("tmsh modify /apm resource network-access " + na + " address-space-exclude-dns-name replace-all-with { " + url_exclude_list + " }") log(2, "Updated " + na + " with latest O365 URL list.") if use_ipv4: for na in na_lists: result = commands.getoutput("tmsh modify /apm resource network-access " + na + " address-space-exclude-subnet { " + ipv4_exclude_list + " }") log(2, "Updated " + na + " with latest IPv4 O365 address list.") if use_ipv6: for na in na_lists: result = commands.getoutput("tmsh modify /apm resource network-access " + na + " ipv6-address-space-exclude-subnet { " + ipv6_exclude_list + " }") log(2, "Updated " + na + " with latest IPv6 O365 address list.") #----------------------------------------------------------------------- # Apply Access Policy and Initiate Config Sync: Device to Group #----------------------------------------------------------------------- for ap in access_profiles: result = commands.getoutput("tmsh modify /apm profile access " + ap + " generation-action increment") if ha_config == 1: log(1, "Initiating Config-Sync.") result = commands.getoutput("tmsh run cm config-sync to-group " + device_group_name) log(2, result + "\n") log(1, "Completed O365 URL/IP address update process.") if __name__=='__main__': main() } # Create directory catch { exec /bin/mkdir -p /shared/o365 # Create the script set scriptname "/shared/o365/apm_o365_update_${app_name}.py" set fh [ open $scriptname w+ ] puts $fh $script close $fh # Make the script executable exec /bin/chmod +x $scriptname } set scriptText "script_${app_name} definition \{ catch \{ exec python $scriptname \} \}" tmsh::create sys icall script $scriptText tmsh::create sys icall handler periodic handler_${app_name} script script_${app_name} interval 86400 first-occurrence [ clock format [clock seconds] -format %Y-%m-%d:23:59:59 ] } presentation { section main { # The entry below creates a large text box that must be filled out with a valid IP Address # For details of APL, look at the iApps developers guide: # https://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/bigip-iapps-developer-11-4-0.html choice debug display "medium" default "Off" {"Off" => "0", "Low" => "1","High" => "2"} optional (debug != "0") { message message1 "Note: logging output goes to /var/log/o365_update" } table access_profile { choice profile display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items apm profile access]" } choice nalist display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items apm resource network-access]" } } choice endpoint display "medium" default "Worldwide" {"Worldwide" => "Worldwide", "USGovDoD" => "USGovDoD","USGovGCCHigh" => "USGovGCCHigh","China" => "China","Germany" => "Germany"} } section config { choice include_exchange display "medium" default "1" {"No" => "0", "Yes" => "1"} choice include_sharepoint display "medium" default "0" {"No" => "0", "Yes" => "1"} choice include_skype display "medium" default "0" {"No" => "0", "Yes" => "1"} choice include_common display "medium" default "0" {"No" => "0", "Yes" => "1"} choice use_url display "medium" default "1" {"No" => "0", "Yes" => "1"} choice use_ipv4 display "medium" default "1" {"No" => "0", "Yes" => "1"} choice use_ipv6 display "medium" default "0" {"No" => "0", "Yes" => "1"} choice o365_categories display "medium" default "0" {"Optimize Only" => "0", "Optimize and Allow" => "1", "Optimize, Allow and Default" => "2"} choice only_required display "medium" default "1" {"No" => "0", "Yes" => "1"} table additional_urls { string url required display "medium" validator "IpOrFqdn" } table additional_ips { string ip required display "medium" validator "IpAddress" } choice dg display "large" tcl { package require iapp 1.1.0 return "[iapp::get_items cm device-group]" } } text { # Entities below set the text for the questions and section names, etc. Make them simple and relevant. main "Main" main.debug "Logging Level" main.message1 "" main.access_profile "Access Profiles / NA List" main.access_profile.profile "Access Profile" main.access_profile.nalist "NA List" main.endpoint "Endpoint" config "Configuration" config.include_exchange "Exchange" config.include_sharepoint "Sharepoint" config.include_skype "Skype" config.include_common "Common" config.use_url "Use URLs" config.use_ipv4 "Use IPv4 subnets" config.use_ipv6 "Use IPv6 subnets" config.o365_categories "O365 Categories" config.only_required "Required Endpoints Only" config.additional_urls "Additional URLs" config.additional_urls.url "FQDN" config.additional_ips "Additional IPs" config.additional_ips.ip "IP/Mask" config.dg "High Availability Device Group" } } role-acl none run-as none } } description "o365 split tunnel update iCall v1" ignore-verification false requires-bigip-version-max none requires-bigip-version-min none requires-modules { apm } signing-key none tmpl-checksum none tmpl-signature none } Tested this on version: 13.01.4KViews0likes0Comments