Backstage Scaffolder Parameters

Published on October 31st, 2024

Parameters are how you get input data from users of Backstage Scaffolder templates. Parameters are defined in YAML alongside your template definition and rendered in a form when you run a template.

Here is the most basic example of a single parameter form field in a template:

---
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: parameters-example
spec:
  owner: roadiehq
  type: example
  parameters:
    properties:
      name:
        type: string
  steps:
    - id: log-message
      action: debug:log
      input:
        message: 'Hello ${{ parameters.name }}'

Screenshot of rendered template

The parameters YAML is based on react-jsonschema-form. You can find the available base syntax options there and YAML examples here.

This page lists some examples from React Json Schema Form in YAML format for how to configure parameter forms in the Backstage Scaffolder.

Testing and iterating

You can test the core RJSF parameters live in Roadie at /create/tools -> Template Form Preview.

parameters-preview

NB: This tool does not work with complex form fields and some syntax that will work in the real Scaffolder like a single page form structured as an object.

Field types

Each parameter can be one of a few types: string, number, array or object.

string

parameters:
  properties:
    name:
      type: string

NB: You can validate strings using a regex with the pattern parameter. See Field Validation below.

number

You can allow the user to enter a number using the number type:

parameters:
  properties:
    size:
      type: number

object

The object allows the collection of more complex types of data from the user. It contains the properties option to add variables to the object as follows:

parameters:
  properties:
    person:
      type: object
      properties:
        name:
          type: string
        age:
          type: number

You may choose to make an object property to be mandatory using the required property.

parameters:
  properties:
    person:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        age:
          type: number

array

You can prompt for an array of properties using the array option. The items option can be any type: array, object, string or number as you like.

parameters:
  properties:
    languages:
      type: array
      items:
        type: string

enums

Enums values can be provided for all these types like so:

properties:
  name:
    title: Name
    type: string
    enum: ['Tom', 'Bob']

Enums render by default with a select dropdown.

Radio and Checkboxes

You can use a radio or checkbox by adding the widget field:

properties:
  name:
    title: Name
    type: string
    enum: ['Tom', 'Bob']
    ui:widget: "RadioWidget"
properties:
  name:
    title: Name
    type: string
    enum: ['Tom', 'Bob']
    ui:widget: "CheckboxesWidget"
Multi select

Multi select can be achieved using the array type:

properties:
  names:
    title: Names
    type: array
    ui:widget: "CheckboxesWidget"
    uniqueItems: true
    items:
      enum: 
        - Tom
        - Bob
Enum Display Names

You can render a different name for your enum values using enumNames:

properties:
  city:
    title: City
    type: string
    enumNames: 
      - New York
      - Amsterdam
      - Hong Kong
    enum:
      - nyc
      - ams
      - hk

This is particularly useful for encoding additional hidden data in your enum values that can be used in later template steps:

properties:
  city:
    title: City
    type: string
    enumNames: 
      - New York
      - Amsterdam
      - Hong Kong
    enum:
      - name: 'New York'
        lat: 40
        lon: 74
      - name: Amsterdam
        lat: 52
        lon: 5
      - name: 'Hong Kong'
        lat: 22
        lon: 114

steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: 'The city ${{ parameters.city.name }} is at ${{ parameters.city.lat }},${{ parameters.city.lon }}'

Field Validation

You can use react-jsonschema-form to perform validation on input fields using a regex in the pattern filed or character counts using maxLength and minLength.

parameters:
  properties:
    name:
      title: Simple text input
      type: string
      description: Description about input
      maxLength: 8
      pattern: '^([a-zA-Z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$'
      ui:autofocus: true
      ui:help: 'Hint: additional description...'
    errorMessage:
      properties:
        name: '1-8 alphanumeric tokens (first starts with letter) delimited by -'

Multi Page Forms

You can choose to break up the parameters into form pages or collect all the parameters in one single page.

Single Page:

parameters:
  properties:
    name:
      title: Name
      type: string
    email:
      title: Email
      type: string
      maxLength: 8
      pattern: '^[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,}$'
    role:
      title: Role
      type: string
    company:
      title: Company Name
      type: string

Multi Page:

parameters:
  - title: Provide some info about yourself
    required:
      - name
      - email
    properties:
      name:
        title: Name
        type: string
      email:
        title: Email
        type: string
        maxLength: 8
        pattern: '^[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,}$'
  - title: Provide some info about your occupation
    properties:
      role:
        title: Role
        type: string
      company:
        title: Company Name
        type: string

In a multipage form, each parameter page must contain title and properties

Complex Form Fields

The following items are enhanced form fields that can be used out of the box to build richer forms for your templates.

Roadie allows you to create your own custom React form components and use them in the same way if you have special use cases.

Entity picker

You can prompt the user with a list of catalog entities using the ui:field: EntityPicker option as follows:

parameters:
  properties:
    entity:
      type: string
      ui:field: EntityPicker

Owned entity picker

Alternatively if you would like the user to only select entities that they already own, you might want to use the OwnedEntityPicker.

parameters:
  properties:
    ownedEntity:
      type: string
      ui:field: OwnedEntityPicker

Entity name picker

If you would like a little validation when the user enters an Entity name, you can use the EntityNamePicker. It will prevent the user from entering an entity name that is not an acceptable entity name.

parameters:
  properties:
    ownedEntity:
      type: string
      ui:field: EntityNamePicker

Repository picker

The respository picker can allow the user to select the name and location of a new repository. The picker restricts the target location of the repository to make it a little easier for the user to select a target location.

The following example, will only allow the user to enter a new repository name targeting the GitHub using the AcmeInc organization.

parameters:
  properties:
    repoUrl:
      type: string
      ui:field: RepoUrlPicker
      ui:options:
        allowedHosts:
          - github.com
        allowedOwners:
          - AcmeInc

The RepoUrlPicker uses the allowedHosts to decide how to build the repo url output value. If you use bitbucket.org it will output a valid repo url for Bitbucket.

parameters:
  properties:
    repoUrl:
      type: string
      ui:field: RepoUrlPicker
      ui:options:
        allowedHosts:
          - bitbucket.org

Owner picker

The owner picker, allows the user to select a user / group in the Backstage catalog. e.g.

parameters:
  properties:
    owner:
      type: string
      ui:field: OwnerPicker

This returns a variable in the format group:<namespace>/<group-or-user-name>. You can extract the entity name using replace when you refer to the parameter like so: ${{ parameters.owner | replace(\"group:.*/\", \"\") }}

Picker from external API source

This custom scaffolder field, makes an API call to the Backstage backend and allows the result to be rendered to a list.

parameters:
  properties:
    custom:
      title: custom
      type: string
      description: Custom field from external API

      # Use `SelectFieldFromApi` to configure the select field for the entry.
      ui:field: SelectFieldFromApi

      ui:options:
        # The Path on the Backstage API and the parameters to fetch the data for the dropdown
        path: 'catalog/entity-facets'
        params:
          facet: 'kind'

        # This selects the array element from the API fetch response. It finds the array with the name kind
        # under the facets object
        arraySelector: 'facets.kind'

        # (Optional) This selects the field in the array to use for the value of each select item. If its not specified
        # it will use the value of the item directly.
        valueSelector: 'count'
        # (Optional) This selects the field in the array to use for the label of each select item.
        labelSelector: 'value'

Some of the SelectFieldFromApi options allow using parameters from earlier parameter pages to be used to template the options. The templated options are params, path, valueSelector and labelSelector. e.g.

parameters:
  - title: Select an Entity kind
    required:
      - kind
    properties:
      kind:
        title: Kind
        type: string
        enum:
          - template
          - location
        default: template
        ui:autofocus: true
        ui:options:
          rows: 5
  - title: Select the specific entity you want
    properties:
      obj:
        title: custom
        type: string
        description: Entity Selector
        ui:field: SelectFieldFromApi
        ui:options:
          path: "catalog/entities"
          params:
            filter: "kind={{ parameters.kind }}"
          valueSelector: "metadata.name"
          labelSelector: "metadata.description"

Adding custom parameters

You can extend and customize the available Scaffolder functionality by using Roadie Custom Plugins and creating a Field extension.

You can find more information on how this is done in the tutorial on the Open Source Backstage documentation page.

Field extensions are react form components that can be imported dynamically into the Roadie Scaffolder. You can use the same workflow and development experience to build your own custom Scaffolder Field Extensions as any other plugin, using the Roadie CLI.

Registering your Field Extension

Scaffolder Field Extensions are automatically registered to be usable within Scaffolder Templates, once they have been registered as a Custom Plugin component in the Roadie application. You can do this by navigating to the Administration -> Custom Plugins page and registering your Custom Plugin bundle, including a component of type ScaffolderFieldExtension.

Custom plugin registration form

Re-using Parameter Pages

You can create re-usable form pages using the placeholders feature like so:

---
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: global-param-example
  title: Example of sourcing params from a constant
spec:
  owner: roadie
  type: service

  parameters:
    - $yaml: ./constants/products.yaml

  steps:
    - id: log-message
      name: List selected product
      action: debug:log
      input:
        message: 'Selected product: ${{ parameters.product }}'

./constants/products.yaml

title: Select Product
properties:
  product:
    title: Product
    type: string
    enum: ["Search", "CRM", "Onboarding"]

NB: This only works for top level parameters objects and not for sections within a form page.

Using Parameters In Actions

Parameters can be retrieved later on by action steps using parameter outputs. Here is an example of a parameter name being used by a debug:log step.

parameters:
  properties:
    name:
      type: string
steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: 'Hello, ${{ parameters.name }}!'

If you need to reference elements of an array parameter you can refer to them using the following syntax:

steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: 'Hello, ${{ parameters.names[0] }}!'

An object parameter values can be reference in the way you might expect. Keys with special characters can be accessed via square bracket notation like so ${{ parameters["something-else"] }}

steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: 'Hello, ${{ parameters.person.name }}!'

Using logic in Parameter references

Inliine logic can be used in these references also via pipe functions using Nunjucks templating syntax.

i.e.

steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: "Hello, ${{ parameters.name | replace('Mr', 'Mrs') | capitalize }}! You have lived at the following addresses: ${{ parameters.addresses | join(", ") }}"

Available filters are listed in Nunjucks documentation here.

Default values

If you would like to default the value of a field you can use the default option:

parameters:
  properties:
    name:
      type: string
      default: 'world!'

Field display options

You can display a more human description to a field value by using title and description

parameters:
  properties:
    name:
      type: string
      title: 'Name'
      description: 'Name to say hello to'