Getting Started with iApps: Best Practices

In this final article in the Getting Started with iApps series, we’ll ship gears from concepts and code to best practices and lessons learned.

Start Small

As stated earlier--iApps are complex. If you don’t know tmsh, or better, tmsh scripting, I’d recommend spending some time getting comfortable at the command line writing scripts before moving to an iApp. Once you have the foundational tmsh skills, start with a simple task like what we covered in the modifying and creating articles in this series. After the pieces start to fit together more clearly, expand from there.

Start Basic

I mentioned the iApp utility package in the previous article. It’s available and it’s awesome. But it also abstracts some of the work that might confuse what’s actually happening in the iApp. I’d recommend you avoid the utility package while you are learning, then once you’re comfortable, convert your test work to the utility package. Afterwards, starting with the utility package unlocks a lot of functionality that will save you time and headaches.


This is a small but important section of the iApp. When developing an iApp, there may be features that exist on your version that either didn’t exist or changed significantly from previous or later versions. Setting the minimum and maximum versions where applicable might save you or others in the event of upgrades or imports. Also, establishing the required modules up front is important, so don’t forget this step.


This is the user interface for those deploying your template, so it’s important to consider not only the required information, but also the layout. Some best practices for the template design:

  • Use a question and answer format
  • Be simple
  • Be clear
  • Be complete
  • Follow your organization’s nomenclature

Gotcha: You may be tempted to expose only a few settings on any particular configuration object, leaving the remaining settings default. But a default when creating objects via the GUI is not necessarily the same default when creating them via tmsh. Example: a virtual server created via the GUI defaults to the tcp profile, but creating a virtual server via tmsh defaults to the fastL4 profile.

Make sure to use the layout elements to your advantage. Use the section element to group like features/functionality in your iApp.

Hide stuff that isn't required for particular deployments by using the optional element. This makes readability much better

Underscores in section variable names are possible, but discouraged. Yes, I broke this rule in the creating an iApp article yesterday! The reason is because the section.variable on the implementation ends up section__variable (double underscore) and if your section also has an underscore, it makes it easy to overlook the differences.

Use validators for input fields where they are supported. These are some common validators:

  • Number:  Any integer
  • NonNegativeNumber: Any integer >= 0
  • IpAddress:  * for all addresses, or an IPv4 or IPv6 address
  • PortNumber: * for all ports, or an integer value between 0 and 65535 inclusive
  • FQDN: RFC 1034 compliant domain name
  • IpOrFQDN: IPv4, IPv6, or an RFC 1034 complaint domain name. * not allowed

Gotcha: validators are not available with the edit choice element, so make sure to add validation on the implementation section for the values returned from these elements.

Also very helpful: you can display user-friendly wording while setting a different value that is passed on to the implementation. This is nice for things like the load balancing method, where least-connections-member isn’t awesome to look at, but is the required text string for the tmsh::pool create/modify command.

Finally, you can test all your variable setting and such by using the 

 command in the implementation section that will log all your values from the presentation section. This information is useful for debugging and is dumped in the /var/tmp/scriptd.out file for review. Examples:

puts $::basic_info__validation
puts $::basic_info__key
puts $::basic_info__rules

[root@ltm3:Active:Standalone] config # tail -f /var/tmp/scriptd.out

test1 test2


The implementation section is pretty much a tmsh script, without the required skeleton. the hierarchy of tmsh, for those not well versed, looks like this:

The general syntax of tmsh commands is:

tmsh <command> <module> [<submodule>] <component> <parameters>

The syntax varies slightly depending on where the commands are issued. For example:

Location where command is runSyntax
tmsh root(tmos) # create /ltm virtual virtual_name...
virtual server component of tmsh(tmos.ltm.virtual) # create virtual_name...
linux bash promptconfig # tmsh create /ltm virtual virtual_name...
iApp implementation sectiontmsh::create "/ltm virtual virtual_name..."
iapp::conf create ltm virtual virtual_name...

In addition to the declared variables from the presentation section, you can set variables in the implementation section as well. This 

 loop shows the use of a local variable (obj) and the presentation declared variable ($::basic_info__rules)

foreach obj $::basic_info__rules {
  exec "tmsh" "generate" "ltm" "rule" $obj "checksum"

There’s also 

 which stores the name of your application service. When creating objects, it’s helpful to name them with your application name as a header. This can be done like so:

set app $tmsh::app_name
tmsh::create “/ltm pool ${app}_MyPool ...

The { } brackets are there so the interpreter to only interprets app when doing the variable lookup. There are a lot more general tcl best practices, but those are out of scope for this article.


iApps were created with serving an application over time, not just a Ron Popeil “set it and forget it” experience. That’s more the point of a wizard. This presents a challenge, as you need to create objects initially, but when you reconfigure your iApp, you’re modifying existing objects. In tmsh, this is problematic: if you try to create an existing object, it’ll complain. Why does this work in an iApp then? 

The answer is, through a process called mark-and-sweep. When the iApp user selects an existing application service and clicks the Reconfigure tab, they are reentering your template and its code. They can keep and/or change their responses to the presentation section prompts. If they make changes that result in a new configuration, the mark-and-sweep process is designed to help transform the old configuration to the new configuration.

On reentrancy, the code in the template’s implementation section is re-executed. Conditional code is executed based on the variable values set by you or by the user’s responses. Any tmsh::create commands that are “touched” are marked and converted to tmsh::modify commands. Any objects that are “untouched” are deleted in the sweep.

Strict Updates

Strike updates protect all the objects generated by an iApp. So if you created an iApp that owns the pool app_myPool, that pool cannot be modified outside of the iApp. This is good when a lot of cooks are in the kitchen and someone inadvertently tries to delete your pool. Because the iApp owns it, that attempt will fail. There are situations where you might be tempted to disable the strict updates, but the better option is to modify the template. If you do disable the strict updates, redeploying the iApp will sometimes overwrite the out-of-band changes. Sometimes. The outcome is not so easily predicted.

To illustrate the difficulties, consider two scenarios.

Changing an HTTP Profile

In this scenario, a simple iApp accepts 2 inputs and constructs a virtual server with a custom HTTP profile:

presentation {
 section virtual {
 string ip required
 choice xff { “enabled”, “disabled” }
implementation {
 tmsh::create ltm profile http ${tmsh::app_name}_http \
 insert-xforwarded-for $::virtual__xff
 tmsh::create ltm virtual ${tmsh::app_name}_vs \
 destination $::virtual__ip:80 \
 profiles add \{ ${tmsh::app_name}_http \}

Suppose you deploy this iApp with X-Forwarded-For enabled, then disable strict-updates and edit the HTTP profile directly as shown:

Now, re-enter and re-deploy the iApp without changing any inputs. What is the result? You might expect your manual customization of response chunking to remain in place, because there is no mention of response chunking in the iApp code. In this case, however, iApp re-deployment causes chunking to be set back to its default value.

Disabling Virtual Server Address Translation

In this scenario, you need to disable address translation on a virtual server, which was not possible in the iApp. Disable address translation, then re-enter and re-deploy the iApp again. What is the result this time? You might expect your customization to be overwritten as it was in the previous example. In this case, however, address translation remains disabled. The TMSH “modify” command does not handle defaults consistently across all key-value pairs. This makes tampering with a strictly-enforced configuration especially hazardous.


iApps are amazingly powerful tools for taking a lot of the guesswork and potential errors out of application deployments. There is so much more to iApps than we could possibly cover in this five article series, but the intention this week was not to make you experts; merely to expose you to the concepts and get your feet wet with some examples. The great thing about iApps is the are not compiled code: all of them, whether F5 supported, F5 contributed, or community contributed, can be analyzed and repurposed for your use. 

Updated Jun 06, 2023
Version 2.0

Was this article helpful?

No CommentsBe the first to comment