Scaffolder templates

Published on May 16th, 2022

Overview

What Scaffolder Templates Are

Scaffolder templates are repeatable, self-service automation for creating and provisioning software and infrastructure.
They help organizations standardize best practices, reduce toil, and improve developer experience. Templates are a key part of the Roadie platform and are used to create new services and components, as well as to modify existing services and infrastructure. They are YAML-defined blueprints that automate the creation of new software components, services, or infrastructure within an engineering organization, as well as engage with existing services and infrastructure to modify them.

A template describes:

  • What to generate (files, directories, config)
  • What parameters to collect from the user (name, owner, repo, runtime, etc.)
  • What actions to perform (create repos, apply IaC, set up CI/CD, register components, etc.)

They are run on demand by the user, which guides input through a form, and then runs defined actions. Roadie will execute the software template in an ephemeral container that is destroyed after the execution completes.

How Templates Are Used

1. Standardize New Services and Components

Teams can ensure every new microservice, library, or UI package follows the organization’s:

  • tech stack standards
  • folder structure
  • security baselines
  • best practices

2. Automate Repetitive Setup

Templates can automate tasks such as:

  • creating a repository in your SCM (e.g. GitHub, GitLab, Bitbucket, Azure DevOps)
  • pushing starter code to the repository
  • applying Terraform modules
  • provisioning cloud resources
  • enabling CI/CD pipelines
  • registering the component in the Roadie Catalog

3. Enable Self-Service Infrastructure

Engineers can request infrastructure or resources such as:

  • databases
  • service accounts
  • enabling or disabling feature flags
  • creating ephemeral environments

The template handles provisioning so engineers don’t need to file tickets or perform manual actions.

4. Enforce Governance and Golden Paths

Templates encode "how we build software here," ensuring:

  • security requirements are met
  • compliance metadata is captured
  • observability and runtime configuration is always added

5. Scale Engineering Workflows

Templates transform long manual checklists into one-click or one-form operations, allowing teams to scale without extra overhead. This is especially useful for:

  • onboarding new engineers
  • performing repetitive tasks
  • ensuring consistency across teams

Parts of a Template

Header Section

The header section is required for every template and contains information to configure the task and show details about the task on the "Create Component" page.

yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: hello-world-template
  title: Hello World
  description: Says Hello to a specified name.
spec:
  owner: default/engineering
  type: service

apiVersion

This is a required field and should be set to scaffolder.backstage.io/v1beta3

kind

A Scaffolder template is also an Entity in Roadie. In order to configure this entity as a template you must set the kind to Template

metadata

The metadata field contains some data that appears on the template card that appears on the "Create Component" page.

spec

The spec field contains owner and type. Owner refers to the Roadie group or user that owns the Scaffolder task e.g. default/engineering. Type refers to the type of template. It can be set to anything and appears on the scaffolder template card in the "Create Component" page.

Form Input

The form input fields and steps are defined in the parameters section of the template. The parameters yaml is based on react-jsonschema-form and you can find the available syntax options there and examples here .

You can choose to break up the parameter prompting into form steps or collect all the parameters in one single step. Each parameter can be one of a few types: string, number, array or object. Here is an example of a single parameter form field:

yaml
parameters:
  - title: Your Name
    required:
      - name
    properties:
      name:
        type: string

parameters

The parameters is an array of objects that define the form input fields and steps. Each object contains the following fields:

  • title: The title of the form step
  • required: An array of required fields
  • properties: An object with the properties of the form field

properties

The properties object contains the form fields to be displayed in the form. Some common fields are:

  • name: This is the field object key that will be used to reference the field in the template.
  • type: The type of the form field
  • title: The title of the form field
  • description: The description of the form field
  • required: Whether the field is required
  • default: The default value of the field

Actions Performed

The steps section defines the actions that are taken by the scaffolder template when it is run as a task. The scaffolder initially creates a temporary directory referred to as the workspace, in which files are downloaded, generated, updated and pushed to some external system. Each step that is defined is run in order.

You can refer to the value of a parameter using the syntax ${{ parameters["name"] }} e.g.

yaml
steps:
  - id: log-message
    name: Log Message
    action: debug:log
    input:
      message: 'Hello, ${{ parameters["name"] }}'

If the parameter id does not contain a special character you can also refer to it using the dot syntax ${{ parameters.name }}

You can find all the available actions to your Roadie instance by visiting the following page from within your Roadie tenant:

https://<tenant-name>.roadie.so/create/actions

Outputs from previous steps

You can refer to the output of a previous step using the following syntax:

yaml
${{ steps["publish-step-id"].output.repoContentsUrl }}

If the step id does not contain a special character you can also refer to it using the dot syntax.

yaml
${{ steps.publish.output.repoContentsUrl }}

Looping

You can use array type form inputs or step outputs to repeat action steps multiple times. e.g.

yaml
- id: log-description
  name: Log Message
  each: ['Brian', 'Ian']
  action: debug:log
  input:
    message: 'Hello, ${{ each.value }}!'

Accessing the logged in user

You can refer to the user entity reference for the logged in user using the following syntax:

yaml
${{ user.ref }}

If this entity reference exists in the Roadie Catalog, you can also make use of the details contained within the users entity by using the following:

yaml
${{ user.entity.metadata.name }}

or access the details contained within the user's profile.

yaml
${{ user.entity.spec.profile.email }}

Conditional Steps

You can conditionally execute a scaffolder action based on an input parameter.

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

Advanced

Calling an internal API

If you need a scaffolder step to contact a custom authenticated service or any public API for that matter that is not currently supported by a built-in action, you can do that using a combination of the http:backstage:request action and a Roadie proxy configuration.

Start by creating a proxy configuration as described in this page

Then you can add a step to call that API using the http:backstage:request action as follows:

yaml
steps:
  - action: http:backstage:request
    id: http-request
    name: Create a thing on the acme service
    input:
      method: POST
      path: "/api/proxy/acme/thing"
  - action: debug:log
    id: log-result
    name: Log the result of creating the thing
    input:
      message: "The response code was ${{ steps["http-request"].output.code }}'

Escaping syntax

If you need to pass variable substitution syntax through without it being interpreted, you can escape the syntax by wrapping it like so ${{ '${{ parameters.something }}' }}.

Creating re-usable snippets

You can inject in re-usable snippets of yaml into a template using the $yaml operator like so:

templates/debug-step.yaml

yaml
- name: Debug log 2
  id: debug_log_2
  action: 'debug:log'
  input:
    message: Second log

logging-template.yaml

yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: placeholder-example
  title: Demonstrating the placeholder usage
  description: Shows how to inject in a single re-usable step
spec:
  owner: default/engineering
  type: service
  steps:
    - name: Debug log 1
      id: debug_log_1
      action: 'debug:log'
      input:
        message: First log

      $yaml: https://github.com/yourOrg/some-repo/blob/templates/debug-step.yaml

This can only be done for a single step as the re-usable section must be valid yaml.

Using a user's Github Token to execute template steps

You can use the user that runs the scaffolder template to open a PR or other Github based actions rather than opening it on behalf of the Roadie Github App by specifying the token field. The token must first be injected via the parameters by the RepoUrlPicker parameter as documented here

yaml
parameters:
  - title: Choose a location
    required:
      - repoUrl
    properties:
      repoUrl:
        title: Repository Location
        type: string
        ui:field: RepoUrlPicker
        ui:options:
          # Here's the option you can pass to the RepoUrlPicker
          requestUserCredentials:
            secretsKey: USER_OAUTH_TOKEN
            additionalScopes:
              github:
                # - admin:org # Needed if you want to create a repo
                - workflow
          allowedHosts:
            - github.com
steps:
  - action: publish:github:pull-request
    id: create-pull-request
    name: Create a pull request
    input:
      repoUrl: 'github.com?repo=reponame&owner=AcmeInc'
      branchName: ticketNumber-123
      title: 'Make some changes to the files'
      description: 'This pull request makes changes to the files in the reponame repository in the AcmeInc organization'
      # here's where the secret can be used
      token: ${{ secrets.USER_OAUTH_TOKEN }}

Alternatively, if you don't need to build a repo URL you can use the OauthSecret field to intiate login only. The token is stored in a secret value in the same way as above.

yaml
  parameters:
    - title: User Login
      required:
        - isLoggedIn
      properties:
        isLoggedIn:
          title: Is logged in
          type: boolean
          ui:field: OauthSecret
          ui:options:
            secretsKey: USER_OAUTH_TOKEN
            host: gitlab.com