31-Mar-2020 13:25 - edited 09-Feb-2022 10:07
This guide will walk through how the logon, webtop, and other UI pages are created by APM, how it works, and some examples.The new APM modern template has an updated look in both mobile and desktop browsers. It uses the popular Preact framework to provide a consistent and familiar end user experience.
New policies created in 15.1 and later default to the new Modern template. Existing policies made before 15.1 continue to use the Standard template.
When creating a new access policy:
You can see the customization types applied to all access policies in the Per-Session access policy menu:
APM has different ways to customize, depending on the desired complexity. Some administrators simply want to change colors, while others would prefer to completely rewrite the APM user-facing HTML and CSS so they have similar branding to other corporate web properties. As is typical with CSS, style customizations can be applied simultaneously with later style changes overriding earlier ones.
Simple customization settings such as images, titles, captions, and colors are applied to resources and policies using Basic Customization. The tables and screenshot below detail the settings.
Header/Footer/Title
Layout Settings
Colors (see image for sections)
General Customization offers a more advanced tree view of the Basic Customization settings as well as text options for user-facing messages. Like Basic customization, the settings are applied individually to each access policy and policy item via the item’s XML file specified in the cache-path parameter of the associated customization group configuration object.
To use General Customization
You use General Customization to apply changes to the informational text strings and error messages that are displayed to users. Browse the configuration tree to see the available customization areas.
The settings are divided into Branding and Text. Text are the localized messages that are displayed to the end users, including associated HTML. User-facing status and text messages can be selected in the Text tab. Branding options are about page styling, colors, fonts, and the like. These are selected in the Branding tab.
APM customization has a few non-branding and non-text special options in the configuration tree.
The new Modern customization includes a new resource loader feature. This feature loads all added CSS and JS resources dynamically, including 3rd party external code. You use this option like a “Revert” to restore the default branding in case there is some unknown trouble. If enabled, it disables loading of all external CSS and JS, including the APM-hosted user-XXXX.js and user-XXXX.css files.
You can now easily add external javascript references, for libraries such as JQuery that you would like your users to load from an external CDN. Historically this would present a security problem because the CDN content may be vulnerable to malicious injection. APM uses the W3C subresource integrity feature(https://www.w3.org/TR/SRI/) mechanism to ensure that the external files are not tampered.
You can also specify external scripts manually in user-XXXX.js, but using the inbuilt APM script-loader mechanism allows us to trap loading errors and disable all external scripts globally, in case of any problems (see the previous section).
To use this feature:
*Note*: APM’s end-user pages are built using Preact with built-in libraries, so don't load another copy of Preact using this mechanism. See the Advanced Examples section below for usage ideas.
If the checksum is incorrect the stock APM javascript will function correctly, but the external resource will not be loaded and the browser will produce a “Failed to find a valid digest in the integrity attribute for resource ‘xxxxxx’” error in the console similar to this screenshot:
* Supported checksum mechanisms are SHA-256, SHA-384, or SHA-512.
Use Advanced Customization to edit or place code directly into the files that are referenced from the primary APM user-facing HTML. Common settings are available which load on all pages, along with separate CSS and JS for each policy item that is present in the policy. Generally, the CSS/JS for each policy item load after the common settings so later settings will override earlier ones.
You must first add policy items to customize before customizing them. This is a customization tree view before and after adding Logon Page to access policy:
Before
After
With Advanced Customization, you can do essentially any styling you want using standard CSS.
Advanced Customization has some common settings, agent settings, and some special settings. After making any change, Save Draft, then Save., then Apply the access policy.
These two files (user-common.css and user-common.js) are loaded on all* APM user-facing pages, including policy evaluation (logon, message, etc), webtop, and logout.
Use these if you want to change a page style in all areas. For example, perhaps we always want to hide the header and footer and add a background image. To do that, we can simply add some CSS to user-common.css to set a few properties targeting the apmui-header, apmui-main, and apmui-footer CSS selectors.
Hide header and footer, and add a picture:
*Note*: You should usually make logon-specific changes on the logon page rather than “common”, since the webtop places some GUI links in apmui-header. Hiding the header removes access to these links!
You can use user-common.js to load a tracker such as Google Analytics.
Example: Google Analytics
There are two broad categories for the customization of APM objects:
Resources are Assigned during per-session access policy execution. They include customizable icons, captions, and descriptions that are visible on the APM full webtop (sometimes called a portal).
Non-Resources each have different configuration properties
*Example*: Simple resource customization of a webtop link resource
End-User view from APM webtop:
Resource customization settings, text strings, and image files are stored as interdependent configuration and file objects. When troubleshooting, check the following configuration areas.
This diagram represents the dependencies:
This table represents the item details, and troubleshooting tips:
NOTE: To create resource customization using scripting or automation, they must be created all at once using a TMSH transaction rather than individually because of the interdependency between resources, profiles, policies, policy items, agents, and customization groups. To get started, use the GUI to create a policy you like, then review the configuration objects defined in tmsh list apm. The objects inter-referred-to must be copied into a transaction. equivalent create apm xxxx commands inside of a transaction. For detail on transactions with APM policies, see
tmsh help apm policy access-policy tmsh help cli transaction
Access profiles, Per Request Policies and other objects (customized independently from an access profile) share similar syntax and structure. Each object has customization settings. Access Profiles have multiple groups of customization settings. Every time you change customization, it generates a set of files that are combined to form the user-displayed page.
Settings (color, font, text, and so on) for the header and footer can be defined in access profile customization. Settings for the location and alignment of the content area can also be defined in access profile customization. Settings for resources displayed in the APM Webtop can be defined in the resource's configuration area (see payroll example above).
To place images or other files in APM for convenient access by end users, use the Hosted Content feature.
This example can be placed into user-logon.js to use the D3 library to display a small pop-up message. Additional code can be inserted for custom functions.
define(["require", "exports", "tslib", "module", "apmui/page/logon/View"], function (require, exports, tslib_1, module, View_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); requirejs.config({ map: { 'apmui/master/View': { 'apmui/page/logon/View': module.id, }, }, }); /* Replacement View component */ var CustomLogonView = /** @class */ (function (_super) { tslib_1.__extends(CustomLogonView, _super); function CustomLogonView() { return _super !== null && _super.apply(this, arguments) || this; } CustomLogonView.prototype.componentDidMount = function () { _super.prototype.componentDidMount.call(this); requirejs(['https://d3js.org/d3.v6.min.js'], function (d3) { // Place your code inside this function d3.select("form").append("span") .text("Hello from D3 library"); }); }; return CustomLogonView; }(View_1.default)); exports.default = CustomLogonView; });
This example can be placed into user-logon.js to perform custom actions.
define(["require", "exports", "tslib", "module", "apmui/page/logon/View"], function (require, exports, tslib_1, module, View_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); requirejs.config({ map: { 'apmui/master/View': { 'apmui/page/logon/View': module.id, }, }, }); /* Replacement View component */ var CustomLogonView = /** @class */ (function (_super) { tslib_1.__extends(CustomLogonView, _super); function CustomLogonView() { return _super !== null && _super.apply(this, arguments) || this; } CustomLogonView.prototype.componentDidMount = function () { _super.prototype.componentDidMount.call(this); alert('PLACE CUSTOM CODE HERE'); }; return CustomLogonView; }(View_1.default)); exports.default = CustomLogonView; });
These use TypeScript and preact You should be familiar with these technologies and their usage. To use these examples, you need a standard TypeScript/NodeJS+NPM build environment. This can be most easily achieved by using Linux, Microsoft Windows Subsystem for Linux (WSL), or Mac, then installing NodeJS which includes NPM.
The examples are attached to this article as a ZIP archive. Download and decompress this file to a suitable location on your workstation.
Read README.md from the package. Each example here assumes you have already downloaded the package and run npm install; npm run build. The compilation result is placed in the dist directory. For each one of the examples, you simply copy the user-xxxx.css and user-xxxx.js files into the correct object in Advanced Customization.
To make changes to these examples, modify the files in src, then npm run build, as specified in README.md.
By default, the APM Decision Box has only two choices.
We can use this advanced customization example to append additional choices. Follow this procedure:
The result: value is the raw POST data, which is transferred from the client browser when the my.policy page is submitted. It must match an agent expression in the branch rules defined for the decision box object. As with all other agents, the branch rules must be defined in the policy-item configuration so that the additional branches are available in the VPE for use:
Once these additional branch rules are defined, they can be added to the VPE flow:
When a user makes a choice, it appears in log files thusly:
To host your own icons, you can use the APM sandbox hosting feature, discussed elsewhere in this doc.
This example adds a custom Preact component to the Logon Box.
This is an example of how to obtain a JSON-formatted dump of data available programmatically.
Add the code from logon-custom-view to your APM policy files, then visit the APM virtual. You will see JSON data that provides the detailed data available.
This is an example of an alternative method of rendering a standard forms logon page with a polymorphic virtual PIN pad. It uses Preact and CSS to achieve this result.
Note: A similar result with a virtual keyboard is possible as well, using other modules available via npm. This requires Preact development.
As with the other examples, compile the TypeScript and place the dist’s user-logon.css and user-logon.js into the APM’s advanced customization area for your logon agent. The result when visiting this logon page is illustrated in this screenshot:
logon-validate-domain contains a sample that has an example of input field validation:
The input validation logic can be changed with the following TypeScript:
This kind of validation logic can be extended for almost any purpose.
Recently-used-resources implements a mechanism that uses local storage to track how many times a resource has been clicked. It also creates a new webtop section that displays most-clicked-on resources. You must have a full webtop and multiple resources assigned to the user (any type is fine).
Use this example like the others: Copy the dist directory files user-webtop.css and user-webtop.js to the advanced customization object and click Save Draft, then Save, and apply the access policy. Note that you may have to clear BIG-IP or browser cache to see the update.
Take a few minutes to examine the browser's Local Storage contents while clicking various favorites assigned to the user.
Most application portals offer some kind of system to save often-used resources at the top of the list. This example uses HTML5 Browser Local Storage to save the user’s resources and render them at the top of the webtop application portal. You must assign a full webtop and multiple resources to the user.
Use this example like the others: Copy the dist directory files user-webtop.css and user-webtop.js to the advanced customization object and click Save Draft, then Save, and apply the access policy. Note that you may have to clear BIG-IP or browser cache to see the update.
Afterwards, logon as a user to the webtop and click the corner pin icon to add the resource to Pinned Resources.
We hope these examples are helpful to performing customization of APM web pages. Please let us know of any further examples that you would like to share!
We have received the following requests for examples and hope to add these soon. Let us know if you can contribute!
F5 Support cannot provide assistance with TypeScript, JavaScript, or Preact coding or web development. To validate the operation of any of these examples, standard web development practices should be used.
These examples include code produced by F5 intended to showcase the possibilities of the v15.1.0 APM customization system and can be used by any APM customer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
As with most open source software, each module in this example includes a separate license (MIT, CC, etc). Before using any of these in a production environment, please review the licensing requirements for each NPM package in node_modules.
Beach scene used in customization examples: Stones in the Beach
By Xavierfsc - Own work, CC BY 4.0, https://commons.wikimedia.org/w/index.php?curid=84430607
Hi Lucas, good article. Should hopefully make things a bit easier to customise APM. I've got a lab running 15.1 but when I try to create a new profile using modern I get the following error.
01070712:3: Values (/Common/modern) specified for Customization Group (/Common/mod-test_general_ui): foreign key index (customization_sourceFk) do not point at an item that exists in the database.
Hi , thanks for trying this out and sorry you're having problems. The original TGZ archive file was created on a Mac using BSD TAR instead of GNU TAR, and these can have problems when used in some platforms. Please try this second archive that was instead prepared using GNU TAR.
http://mail.seattlehq.org/customization-examples2.tgz
Because this is hosted on a private server out of F5's control, please checksum the file before use.
SHA512SUM:
sha512sum customization-examples2.tgz
e2abc0aaccc022640ca0ebd0f20d89d164b95f97953f4589b3c20aff3f33154b9d838c5cb9186da1368004feb2120f6cd31266ff4aaacdd343fec03f78de21d2 customization-examples2.tgz
Please let us know if this resolves your issue and we'll go ahead and update the original archive.
Hi all,
The modern login page and look-and-feel was most often requested by our customers, thank you for this!
But... How can a customer migrate a complex apm config to the new modern type without clicking the full apm policy together again?
The effort to create everything again is huge at some customers because they're using many apm policies for different tasks.
Thanks,
Peter
Agreed. This can often be complicated. The primary problem with this is that Modern/Standard use different kinds of customization-group files, and most access policy configuration objects have a customization-group associated with them. The relationship between the objects (start / next-item, etc) are the same between the two though.
You could manually re-assemble a policy like this (by editing the bigip.conf and the filestore customization files) but that is also fairly complicated.
Hi ,
Thanks for your reply in this.
Just today I was talking to a customer which is using guided-configuration.
He also want to use the modern customization type in gc.
I just tested it and it seems that gc is only creating standard customization type as default. Also there's no possibility to go into advanced config and change that at installation time.
Do you know the state of the support for gc and modern customization style?
Guided Configuration v7 fixed some of these, so if you're running v15.1 or v16.0 and AGC v7.0 it should allow Modern it at least the following sections:
If you find it's missing there or if you need that in additional sections, please let us know by opening a support ticket and requesting it.
Hi
For your information...
According to F5 Support, the Modern customization type in AGC will only be supported since v16.0.
It is written in the release notes which (shame on me) I was not looking at at first:
"Guided Configuration now includes modern customization features and the ability to select a previously configured virtual server."
Hello, regarding open request:
I've been answered in this thread with a working example which I wanted to share.
Is there a document that has the .apmui- options/components?
I'd like to edit the webtop, application link text specifically. Have a background that requires white text to see it, but then I can't see the text in the application link.
Thanks for the question!
To change style related things like this, use CSS. This screenshot illustrates how to use **Inspect** in Chrome's dev tools to figure out the selector and test it. After you test your change, add it to the object's corresponding user-css file in Advanced Customization.
Hi!
How can we adjust the "Execute local javascript code inside Logon Page." general example so we can perform custom actions on full webtop, after browser rendering is complete?
I want to target and add elements inside each apmui-webtop-resource with javascript.
Unfortunately I am not able to find my way through the specific examples you have for webtop.
Thanks!
Hello,
Very helpfull thanks.
Just to share with the community the following example that allows you to read a session variable from the "user-logon.js".
In the "standard" customization manner the syntax in the "logon.inc" is:
var plateformOS = "%{session.custom.last.platform}";
In the "modern" way, it is similar you can use in the "user-logon.js" file following:
newDiv.innerHTML = '<img id="os_img" src="%{session.custom.last.platform}" height="30" width="30">';
CCL => same syntax works also in the modern customization
Hi @krazzy522~ Just to let you know, we've contacted the author and he's working on it. He needs to validate the solution before sharing unless other DC Members have the code?
Didn't want to leave you hanging. 🙂
ps
Dear PSilva
I have added div to JS file like below and added CSS to the div
thank you for your time and support
var divlogo = document.createElement("div");
divLogo.id = "logo";
document.body.appendChild(divLogo);
var img = document.createElement("img");
img.setAttribute("src", "logo.png");
divLogo.appendChild(img);