Upgrading to v7

What to change while still on 6.5, and what changes when you move to version 7

  1. Before you upgrade (on 6.5)
    1. Forms live in their own files
    2. The table field
    3. Renamed properties
    4. Datasources and data schemas
    5. API v1
  2. What changes in 7
    1. Playbooks run on an RTE
    2. What the RTE image contains
    3. The containers run as uid 1000
    4. Launch validation is on
    5. Playbooks no longer see the app’s settings
    6. Only the admin role manages runners and admins
    7. The default password must be changed
    8. Tokens end at logout and password change
    9. Entra ID needs your tenant
    10. Passwords have a minimum length
    11. Run the containers with a restart policy
    12. AWX connections and the home link
    13. Deprecated in 7, removed in 8
  3. Image tags
  4. Rolling back

Version 7 drops everything 6.x deprecated; fix it first on 6.5, where old and new both work, then upgrade.


Before you upgrade (on 6.5)

Forms live in their own files

Forms live only in the forms folder (FORMS_FOLDER_PATH) or a forms repository; config.yaml keeps categories, roles and constants.

  • A forms.yaml file: rename it to config.yaml. On 6.5 the settings page has a Convert forms.yaml to config.yaml button for this.
  • A forms: section in the base config: version 7 rejects it. Move each form to its own file, for example by dragging it to a file in the 6.5 designer.
  • Environment variables:

    Removed Use instead
    FORMS_PATH CONFIG_PATH and FORMS_FOLDER_PATH
    ENABLE_FORMS_YAML_IN_DATABASE ENABLE_CONFIG_IN_DATABASE

The table field

Version 7 rejects table fields, and their properties (tableFields, insertColumns, readonlyColumns, tableTitleAdd, tableTitleEdit) go with them: use a list field with a subform, and turn each column’s from into an expression on __parent__ (see the FAQ).

Renamed properties

Each removed property has a direct replacement:

Removed Use instead
disableRelaunch: true (form) allowRelaunch: false
noOutput: true (field) output: false
enableLogin (role option) allowLogin

Datasources and data schemas

The datasource imports and their admin pages have been removed, as has the AnsibleForms Galaxy collection, whose modules only served them.

  • Scheduled imports stop and the datasource tables are dropped: note down what you need first.
  • The data schemas an import filled are databases of their own and stay. A form that queries them through a credential keeps working.
  • To keep that data fresh, run the import as a playbook of your own, for example from a schedule.

API v1

/api/v1/* has been removed; use /api/v2/* (interactive docs at /api/v2/docs). The calls scripts typically use are:

v1 v2
POST /api/v1/auth/login POST /api/v2/auth/login
POST /api/v1/token POST /api/v2/token
POST /api/v1/job POST /api/v2/job

v2 responds with HTTP status codes (400, 403, 404, 422) and a plain JSON body, not v1’s { status, message, data } envelope.

Single sign-on callbacks too. A redirect URL with /api/v1/ no longer answers: the sign-in returns to the login page without signing in. Change it to /api/v2/ in two places, on the SSO provider in AnsibleForms and in the identity provider (the Entra ID app registration’s redirect URIs, the OIDC client’s valid redirect URIs):

  • Entra ID: https://<your host>/api/v2/auth/azureadoauth2/callback, or /api/v2/auth/entraid/callback from 7.3.0
  • OIDC: https://<your host>/api/v2/auth/oidc/callback

What changes in 7

Version 7 changes how playbooks run and tightens several defaults. Check each item below when you move from 6.5.

Playbooks run on an RTE

Version 7 no longer runs ansible-playbook in the AnsibleForms container. Playbooks run on a runtime environment (RTE), a container of its own, and the app image no longer contains ansible or python. Start an RTE next to the app, from the ghcr.io/ansibleforms/ansibleforms-rte-full:7 image. Since 7.3.0 the app dials the RTE and keeps one WebSocket open (see Modes): the RTE gets no database settings and no ENCRYPTION_SECRET. Its settings:

Setting Value
AF_ROLE rte. The RTE images set it already.
RTE_MODE inbound: the app dials the RTE. A Runner in another network uses outbound (see Modes).
RTE_TOKEN A token of at least 16 characters. The app signs in to the RTE’s socket with it, and the RTE refuses to start without one.
RTE_FILES local when the RTE mounts the same persistent folder as the app, as next to it.

Then move the ansible mounts from the app container to the RTE:

  • ansible.cfg at /etc/ansible/ansible.cfg, your roles at /etc/ansible/roles and your collections at /etc/ansible/collections.
  • Mount the same persistent folder as the app on the RTE too, so that it finds the playbooks and the repositories.

Then declare the RTE as a runner, in the config seed as the Docker Compose project does, or under Connections → Runners: an RTE credential with the RTE’s address (for example http://rte:8000) and the same token, and a runner of type RTE, mode inbound, that names it:

credentials:
  items:
    - name: rte
      credential_type: rte
      url: http://rte:8000
      password: ${RTE_TOKEN}
runners:
  items:
    - name: rte
      type: rte
      rte_mode: inbound
      credential: rte
      is_default: true

The seed reads RTE_TOKEN from the app’s environment. See Runners and Runtime environments.

The same contract for the app and the RTE The app resolves a job’s credentials and hands them to the RTE sealed for that RTE (RTE contract 2, new in 7.0.0). An RTE of another contract refuses the app’s jobs: run the RTE image of the same release as the app, and upgrade both together. Later 7 releases keep working with an older 7 RTE as long as the contract stays the same (see Scaling).

What the RTE image contains

The RTE comes in three layers, each built on the one before. Pick the one that fits your playbooks:

Image Contents
ansibleforms-rte-full The default. Ansible 14 with ansible-core 2.21, and the collections and python libraries the 6.5 image had.
ansibleforms-rte-core Python 3.14, ansible-core and a few common libraries, without collections. Add your own in an image built on it.
ansibleforms-rte-base The server only, without python or ansible, to build an RTE with your own versions.

Moving from 6.5 (ansible 12 with ansible-core 2.19) also changes these for your playbooks:

  • ansible-core 2.21: read the porting guides for 2.20 and 2.21.
  • Python 3.14: the RTE layers run on python 3.14.
  • No more boto: the old boto 2 library is gone. The amazon.aws collection uses boto3, which is included.

Two environment variables now belong to the RTE and are no longer read by the app, so set them in the RTE container:

Variable Change
ANSIBLE_PATH The playbook folder, on the RTE.
PROCESS_MAX_BUFFER Now 50 MB by default. Past it, the job output is no longer stored (the job says so), but the playbook goes on instead of being stopped.

The containers run as uid 1000

The app and RTE images run as the user node (uid 1000) instead of root, and their home folder is /home/node:

  • Give uid 1000 the volumes, once. For the persistent and ssh volumes, run docker run --rm -v <volume>:/v alpine chown -R 1000:1000 /v; for a host folder, sudo chown -R 1000:1000 <folder>. On Kubernetes, set fsGroup: 1000.
  • Mount the ssh folder at /home/node/.ssh, not /root/.ssh. If you set HOME_PATH to /root, remove it or set it to /home/node.
  • Your own RTE image built on an RTE layer switches to USER root for its installs and back to USER node at the end.

Launch validation is on

LAUNCH_VALIDATION now defaults to enforce (6.5: off). Every launch, from the browser or from POST /api/v2/job, is checked against the form’s rules, and the job runs the extravars and credentials the server builds from the checked values.

  • REST callers must send rawFormData, the raw field values as the browser sends them. A launch with only extravars is refused, because its values cannot be checked.
  • To see what would be refused first, set LAUNCH_VALIDATION=log and read the warnings in the log. off restores the behaviour of 6.5.

See Launch validation for the details.

Playbooks no longer see the app’s settings

A playbook no longer inherits the environment variables AnsibleForms reads itself: ENCRYPTION_SECRET, the DB_* settings, ACCESS_TOKEN_SECRET, RTE_TOKEN, the VAULT_* settings, every other documented variable and every MYSQL_* variable. The rest of the RTE’s environment, such as PATH, proxies, ANSIBLE_* settings and cloud credentials, is passed as before.

A playbook that read one of these with lookup('env', ...) must get the value another way: pass it as an extravar or a credential, or set it on the RTE under another name.

Only the admin role manages runners and admins

A user with settings access no longer has full control over who is an admin and where credentials go:

  • Runners: only the admin role adds, changes or deletes a runner, because a runner receives the credentials of the jobs it runs. Users with settings access can still see and test runners.
  • Admin accounts and groups: only the admin role grants admin. Without it, a user cannot put an account in a group the admin role names, nor change, reset or delete an admin account or such a group.
  • Roles: a designer without the admin role cannot change the roles. A save or a restore whose roles differ is refused.

Give the admin role to whoever manages runners, admins and roles.

The default password must be changed

A user who signs in with the public default password AnsibleForms!123 must change it first. Until then, every other API call answers 403 with the code password_change_required, and the browser shows the change-password page.

Sign in once and choose a new password, or set ADMIN_PASSWORD before the first start. Scripts that log in with the default password must use another one.

Tokens end at logout and password change

A logout now ends the tokens of that session, and a password change ends every session of that user, so sign in again after changing a password.

An API token requested with ?expiryDays= lives at most API_TOKEN_MAX_DAYS days (90 by default). Scripts that use long-lived API tokens must request a new one before it expires.

Entra ID needs your tenant

Entra ID (Azure AD) sign-in now uses OpenID Connect against your own tenant only, and a user of another tenant cannot sign in. A provider without a tenant id, or with common, organizations or consumers, is refused.

  • Set the tenant id on the SSO provider: the Directory (tenant) ID, a GUID, or the tenant’s domain.
  • Grant the Graph permissions: the login asks for openid profile email User.Read GroupMember.Read.All. Give the app registration the delegated Microsoft Graph permissions User.Read and GroupMember.Read.All, with admin consent.
  • Prefer group ids in roles: name a group as azuread/<object id> (entraid/<object id> from 7.3.0) rather than by its display name.

See Entra ID for the setup.

Passwords have a minimum length

A local password set through the application needs at least PASSWORD_MIN_LENGTH characters (8 by default), and cannot be the username or the default password. PASSWORD_MIN_LENGTH=0 turns the length check off. Existing passwords keep working.

Run the containers with a restart policy

An uncaught exception now ends the process, logged at level crit, instead of leaving it running in an unknown state. Run the containers with a restart policy, such as --restart unless-stopped with Docker (the docker-compose project sets it). Kubernetes restarts the pods by itself.

Two more things are gone in 7, without anything to do on 6.5:

  • AWX and AAP connections: the awx table, /api/v2/awx and the A.A.P. page are gone. The upgrade moves every AWX or AAP connection to Connections → Runners, as a runner of type awx, and forms with awx: keep working.
  • NAV_HOME_LABEL and NAV_HOME_ICON: no longer read. The first link of the header is always Forms.

Deprecated in 7, removed in 8

These still work in 7, but each logs a warning when it is used. Move to the replacement before version 8:

Deprecated Use instead
VAULT_* environment variables A secret store named vault (Connections → Secret stores). The first 7 start imports the variables once as that store, then ignores them.
LOG_SYSLOG_* environment variables A syslog server (Connections → Syslog). When LOG_SYSLOG_HOST is set, the first 7 start imports them once as the server syslog, which gets the server log as before, then ignores them. To send the audit entries too, set it to receive both.
vault_path on a credential (API, config seed) secret_store and secret_ref. See Secret stores.
The role public in config.yaml The role default. The file is renamed when the configuration loads; rename a ytt template by hand. public in a form’s roles: keeps matching.
POST /api/v2/config/vault/check, GET /api/v2/config/vault/mounts POST /api/v2/secretstore/{id}/check, GET /api/v2/secretstore/{id}/mounts
hasApproval on a form or a step approval. hasApproval has no effect.
awx: <name> on a form runner: <name>. An AWX connection is a runner of type awx.
type: multistep on a form, with its steps type: workflow, with nodes that name a runner. See Workflow forms.
The awx: section of the config seed runners: items with type: awx
LOCK_PATH Nothing: no longer read, the designer lock is kept in the database.
An RTE in database mode, since 7.3.0: RTE_MODE=database or no RTE_MODE, with the database’s DB_* and ENCRYPTION_SECRET on the RTE RTE_MODE=outbound or RTE_MODE=inbound, without DB_*. The RTE joins once with a join token (RTE_JOIN_TOKEN), or its runner is set to that mode under Connections → Runners or in the config seed (rte_mode). See Modes.
RTE_REGISTER, since 7.3.0 (database mode only) A join token (RTE_JOIN_TOKEN): the RTE adds itself as a runner that signs in with an RTE credential.
An api or userpass credential on a runner of the config seed, since 7.3.0 A credential of the runner’s kind (credential_type: rte, awx, aap, ascender, semaphore, rundeck or jenkins), with its server in url.
A runner of the config seed with its own token, or username and password, since 7.3.0 A credential of the runner’s kind in the credentials section, with its server in url, that the runner names with credential:. The seed makes one named after the runner meanwhile.

ENABLE_SSO and SSO_AUTO_LOGIN are no longer read since 7.3.0. The first 7.3 start carries them over once: ENABLE_SSO=0 disables every SSO provider, and SSO_AUTO_LOGIN=1 ticks Skip the login page on the only enabled provider. They are then ignored: each start logs a warning while they are set, and each provider has Enabled and Skip the login page under Access → SSO.


Image tags

latest moves to 7 with 7.0.0. To stay on 6, use the tag 6 (or 6.5) instead of latest. All tags are listed under Image tags.


Rolling back

Take a backup before you upgrade (Settings → Backups). 6.5 runs on a database upgraded to 7, but only the backup restores the dropped datasource definitions.