Frequently Asked Questions

More information and examples

Expressions The GUI

Search

Make cascaded dropdowns

enum fields (AKA dropdown boxes) can contain placeholders in their query or expression property in the format of $(another_field) or $(another_field[0].name).
The moment the referenced field changes, the referencing field gets re-evaluated, resulting in dynamic and cascading dropdown boxes.
The power of this concept lies in the client web-application that is re-evaluating fields every 100ms. With current processors and the chromium engine, this should be a very seamless experience.

Note: When you reference another enum field, you reference the selected values, NOT the full dropdown list. Use the placeholderColumn-property or a dot-notation like $(city.name) New in v4.0.20 : setting placeholderColumn to “*” will output the entire record, instead of a single column.

- type: enum
  dbConfig: 
    name: CONN1
    type: mysql
  query: select name from cmdb.city
  name: city_name
  label: Select a city
  default: Amsterdam
  required: true
  model: cmdb.city
  group: CMDB
- type: enum
  dbConfig: 
    name: CONN1
    type: mysql
  query: select datacenter.name from cmdb.datacenter,cmdb.city where datacenter.city_id=city.id
    and city.name='$(city_name)'
  name: datacenter_name
  label: Select a datacenter
  default: __auto__    # default can be "__auto__" (first item) or "__all__" (all items) or "__none__" (no default)
  multiple: true
  outputObject: true
  required: true
  model: cmdb.datacenter
  group: CMDB      

# or use the placeholderColumn property

- type: enum
  dbConfig: 
    name: CONN1
    type: mysql
  query: select id,name,description from cmdb.city
  name: city
  label: Select a city
  default: Amsterdam         # evaluated against valueColumn
  required: true
  model: cmdb.city
  group: CMDB
  valueColumn: name          # we choose the name column as value for placeholders
  placeholderColumn: id      # we can reference the id by using $(city)
  previewColumn: description # when you select a value, the dropdown will show description
  columns:                   # we hide id
  - name
  - description
- type: enum
  dbConfig: 
    name: CONN1
    type: mysql
  query: select name, capacity_pct from cmdb.datacenter where datacenter.city_id=$(city)
# this cascaded dropdown will react using "id" as placeholder
# or "query":"select name from cmdb.datacenter where datacenter.city_id=$(city.id)",  
# or reference the column in the placeholder                            
  name: datacenter_name
  label: Select a datacenter
  default: __auto__         # default can be "__auto__" (first item) or "__all__" (all items) or "__none__" (no default)
  multiple: true
  pctColumns
  - capacity_pct
  outputObject: true
  required: true
  model: cmdb.datacenter
  group: CMDB

Reference another fields value

Placeholders are references to other fields in the forms.
A placeholder is always in the format $(reference). Expressions or queries can contain placeholders.
If the placeholder is pointing to a simple field (text, number, password), it will hold that fields value.
If the placeholder is pointing to a object-based-enum field, then you must either use the placeholderColumn-property or a dot-notation like $(city.name).
If the placeholder is pointing to an expression field, then either the full object is returned or you can have an advanced placeholder reference like $(myarray[0].name) where you can create javascript-like references.

Note: Important to know is that the placeholder is replaced BEFORE the evaluation of the expression. If you expect the result to be a string, then you must wrap it with quotes!. New in v4.0.20 : setting placeholderColumn to “*” will output the entire record, instead of a single column.

- name: field1
  type: expression
  expression: "[{name: 'foo'},{name: 'bar'},{name: 'ansible'}]"
  runLocal: true
- name: field2
  type: expression
  expression: "'$(field1[0].name)'"  # result : 'foo' (note the wrapping quotes)
  runLocal: true
- name: field3
  type: expression
  expression: "$(field1)[0].name"  # result : {name: 'foo'}.name => 'foo'  
  runLocal: true    
- name: field4
  type: expression
  expression: "$(field1).slice(-1)[0].name"  # result : {name: 'ansible'}.name => 'ansible' 
  runLocal: true
- name: field5
  type: enum
  expression: $(field1).filter(x => x.name.includes('a'))   # result : [{name: 'bar'},{name: 'ansible'}]
  runLocal: true
  default: __auto__                                         # result: bar
- name: field6
  type: expression
  expression: "'$(field5)'"   # result : the selected item from field5 (default=bar)
  runLocal: true        

Hide a field

You can hide a field using the field property hide .
Or you can show/hide a field dynamically using the field properties dependencies and dependencyFn.

Group fields together in a block

Use the field property group . Fields with the same group name will be grouped in a block.

Validate a field

Have a look at the many validation field properties such as regex, minValue, notIn, …

Pass credentials

Credentials can be add in several ways.

  • using the field-property asCredential
  • using the credentials form-property (key-value pairs)
  • using an extravar called __credentials__
# assume you have 2 credentials created in Ansible Forms
# 1: vcenter
# 2: ad

# you want them exposed to the playbook as
# 1: vc_cred
# 2: ad_cred

# Method 1 : using asCredential field-property
fields:
- name: vc_cred
  type: expression
  runLocal: true
  expression: "'vcenter'"
- name: ad_cred
  type: expression
  runLocal: true
  expression: "'ad'"

# Method 2 : using credentials form-property   
name: myplaybook
type: ansible
credentials:
  vc_cred: vcenter
  ad_cred: ad
  veeam_cred : veeam_prod,veeam_dev # will first try veeam_prod, then as fallback veeam_dev

# Method 3 : using __credentials__ extravar
fields:
- name: __credentials__
  type: expression
  runLocal: true
  expression: "{vc_cred: 'vcenter',ad_cred: 'ad',veeam_cred:'$(veeam_server)'}"
  # note : in the expression you can use placeholders to make them dynamic

Pass the current jobid

Ansible Forms automatically sends the current jobid in the extravars.
You don’t need to do anything.
It is sent as __jobid__.

Pass the current user

Ansible Forms automatically sends the userinformation in the extravars.
You don’t need to do anything.
It is sent as ansibleforms_user.

Access current user info in the form

Version4.0.2

The field __user__ is automatically added in the form.

expression: $(__user__)
expression: "'$(__user__.username)'"
expression: $(__user__.groups)
expression: $(__user__.roles)

Customize Ansible Forms

Ansible Forms is a web-app. If you run it natively in nodejs, you could replace files or change them.
But more recommended is to run it as a docker-image and add volume or file mappings. Our docker-compose projects already maps directories to make the database, playbooks, logs, certificates and ssh-keys persistent. Nothing is keeping you from adding more mappings to, for example, override the logo.
There is also a custom.js file where you can add your own javascript functions to use in expressions. Just like you can address our functions with the prefix fn. (fn.fnRestBasic for example) you can access the custom functions with prefix fnc..
And you can add your own jq definitions as well in the same way with the jq.custom.definitions.js

volumes:
  # Mount application folder to host folder (to maintain persistency)
  - ./data:/app/dist/persistent
  # Map custom functions for js expressions and jq
  - ./data/functions/custom.js:/app/dist/src/functions/custom.js
  - ./data/functions/jq.custom.definitions.js:/app/dist/src/functions/jq.custom.definitions.js
  # Map custom sshkey to local node .ssh location
  - ./data/ssh:$HOME_DIR/.ssh
  - ./data/git/.gitconfig:$HOME_DIR/.gitconfig    
  # Map custom logo
  - ./data/mylogo.svg:/app/dist/views/assets/img/logo_ansible_forms_full_white.svg

Enum default value

There is obviously the field property default you can use to manipulate a default.
And with enum fields, you can use __auto__ for example to automatically select the first item.

But sometimes you want to have a dynamic default, based on an expression.
See the below example how we accomplish this.

# using client javascript manipulation

- type: expression
  expression: "[{name:'bert'},{name:'ernie'},{name:'pino'}]"
  name: dropdownsource
  label: Dropdown source
  runLocal: true
- type: expression
  expression: "'pino'"
  name: dropdownsourceDefault
  label: Default source
  runLocal: true
- type: enum
  expression: "[...[{name:'$(dropdownsourceDefault)'}],...$(dropdownsource).filter(x => x.name!=='$(dropdownsourceDefault)')]"
  name: dropdownwithdefault
  label: Example with expression default by moving it to top
  runLocal: true
  default: __auto__     

# explained : 
# we take our default and merge it with the source where we filter out the default (to avoid doubles)    
# the default is thus shifted to the first element, which we can now select with the default `__auto__`

Expression default value

For text, number or date fields, you can use the default property to set a default value.
But what if you want this to be dynamic? Like an expression?

You can do this in 2 ways:

  • editable: use the editable property to make an expression-field editable
  • evalDefault: use the evalDefault property to evaluate the default as an expression
# using editable
- type: expression
  expression: "'$(some_other_field)'.toLowerCase()"
  name: field1
  runLocal: true
  editable: true # this will add an edit-button so you manually overwrite the expression value

# using evalDefault
- type: text
  name: field1
  default: "'$(some_other_field)'.toLowerCase()"
  evalDefault: true # it will treat the default as if it was an expression.
  # note : when `some_other_field` changes, the default will be re-evaluated
  # note2 : works for other fields too.

Query information from AWX or Tower

Sometimes you want to create dropdown boxes, with data from AWX or Tower.
You can use fn.fnRestBasic or fn.fnRestJwtSecure to query to do this.

name: Query awx
type: awx
template: my_template # will be overwritten by the field __template__
description: ""
awxCredentials:
  - vmware
executionEnvironment: my_execution_environment
roles:
  - public
categories: []
inventory: my_inventory # will be overwritten by the field __inventory__
tileClass: has-background-info-light
icon: bullseye
fields:
  # make sure you add credentials called "awx_rest" where the password holds the token
  # if you like basic authentication, switch the expressions below to fn.fnRestBasic instead

  - name: organization
    label: Organization
    type: enum
    default: __auto__
    expression: "fn.fnRestJwtSecure('get','https://172.16.50.1/api/v2/organizations','','awx_rest','[.results[]]')"
    columns:
      - name
    valueColumn: id # => we want the organisation field to hold the id !!
  - name: __template__  # use this special name to override the template from the form
    label: Inventory
    type: enum
    default: __auto__
    expression: "fn.fnRestJwtSecure('get','https://172.16.50.1/api/v2/job_templates?organization=$(organization)','','awx_rest','[.results[]]')"
    columns:
      - name
    valueColumn: name
  - name: __inventory__  # use this special name to override the inventory from the form
    label: Inventory
    type: enum
    default: __auto__
    expression: "fn.fnRestJwtSecure('get','https://172.16.50.1/api/v2/inventories?organization=$(organization)','','awx_rest','[.results[]]')"
    columns:
      - name
    valueColumn: name    
  - name: __awxCredentials__  # use this special name to override the credentials from the form
    label: Inventory
    type: enum
    expression: "fn.fnRestJwtSecure('get','https://172.16.50.1/api/v2/credentials?organization=$(organization)','','awx_rest','[.results[]]')"
    multiple: true
    default: __auto__
    columns:
      - name
    valueColumn: name     
  - name: __executionEnvironment__  # use this special name to override the executionEnvironment from the form
    label: Inventory
    type: enum
    expression: "fn.fnRestJwtSecure('get','https://172.16.50.1/api/v2/execution_environments?organization=$(organization)','','awx_rest','[.results[]]')"
    default: __auto__
    columns:
      - name
    valueColumn: name           

Database upgrade to 8.x

At some point you will want to upgrade the MySql database.
Initially, Ansible Forms came with MySql 5.7, but you can upgrade the database of course.
With docker-compose you can simple change the mysql version number in the docker-compose.yaml file. (the current docker-compose file uses 8.1) After a docker-compose down/up, the new image will be pulled and installed.
The upgrade is completely transparent, however, after an upgrade from 5.7 to 8.x you will get warnings about caching_sha2_password, a newer method of password handling.
To remove the warning you need to do 2 steps:

1) Connect to your mysql server and run this :

ALTER USER 'root'@'%' IDENTIFIED WITH caching_sha2_password BY 'MyPassword';
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'MyPassword';

NOTE : Obviously, you need to correct the username and password if you use another connection user and or password.

2) In the my.cnf file, you need to change this :

[mysqld]
default_authentication_plugin=caching_sha2_password

NOTE : in the docker-compose solution the location is ${your-docker-compose-path}/data/mysql/my.cnf

About repositories

Sometimes you want collaboration and versioning and then git repositories are perfect.
In version 5.0.0 you can now manage git repositories.
Every repository is a subfolder of the repositories-path (REPO_PATH environment variable).
Just go to settings / repositories and start managing repositories. You can either add ssh-based repositories (with public key/known hosts) or https based repositories, public or private with username/password/token.
You can use repositories for synching data-files (yaml files for example to feed formdata). But also the forms.yaml and forms subfolder, as well as all the ansible playbooks and roles are perfect candidates for repositories.
When creating a repository, you can choose if it’s for forms or playbooks, which will then automatically override the FORMS_PATH and ANSIBLE_PATH environment variables.
You can also choose if the repository must be cloned when AnsibleForms starts, and you can add cron-schedule to schedule recurring pull-actions.
Additionally, in the swagger interface, you will find a clone and pull rest api for webhooks.
In case you want long-lived access tokens for the webhooks, with swagger you can pass an expiryDays parameter (for admin roles only) and create long-lived tokens.

Enable ytt

In the case you want to use ytt, it can be enabled by setting USE_YTT=1.
Read more info about ytt (https://carvel.dev/ytt/).

NOTE : when using ytt, you must disable the designer, the designer will convert the yaml files to intermediate json and will drop the ytt syntax (which is yaml comments).

A lib directory needs to exist within the root directory and is automatically included for the ytt call. Data can be provided globally by setting prefixed environment variables:

YTT_VARS_PREFIX=YTT_VAR
YTT_VAR_INVENTORY_PATH=/tmp/inventory.yml
YTT_VAR_default_host=localhost

Or by providing library data files:

YTT_LIB_DATA_DEMO=/tmp/demo_data.yml
# /tmp/demo_data.yml
message: 'hello demo'

The library demo needs to exists in the ytt context (lib/_ytt_lib/demo/values.yml)

# lib/_ytt_lib/demo/values.yml
#@ data/values
---
demo: {}

Then, the loaded data can be used:

# forms.yaml
#@ load("@ytt:data", "data")
#@ load("@ytt:library", "library")
#@ demo = library.get("demo")
---
categories:
  - name: Default
    icon: bars
roles:
  - name: admin
    groups:
      - local/admins
constants:
  data_values: #@ data.values
  demo: #@ demo.data_values()
forms:
  - name: Demo Form
    type: ansible
    inventory: #@ data.values.INVENTORY_PATH
    playbook: dummy.yml
fields:
  - name: var_message
    type: text
    label: Message
    default: Object($(demo)).message
    evalDefault: true