Internal Developer Platforms – Part 14: Backstage Software Templates

Table of Contents:

Tags:

In the last few blog posts, we dived into the core functionalities and features of Backstage. That included the Backstage Software Catalog, Backstage Search, or Techdocs. Another great part of Backstage is the Software Template functionality and we will cover that in this and the next blog posts.

What are Software Templates in Spotify Backstage

Backstage Software Templates allow users to create new software components effortlessly. These templates can handle various tasks, such as accepting parameters, processing them, generating skeleton code, creating a GitHub repository, and more.

Users can access these templates by clicking the „Create…“ button in the navigation bar. This leads to the „Create a New Component“ page, where all available templates are listed. Additionally, all templates are registered as entities of type „kind: Template“ in the Software Catalog and can be executed from their respective detail pages.

How can we register existing templates

As described, Backstage Software Template can be registered by using the graphical user interface. Another way is to reference them in app-config.yaml. Those templates can be either loaded from a file location, or better from a Git Url. Let’s take a look in the default app-config.yaml:

catalog:
  import:
    entityFilename: catalog-info.yaml
    pullRequestBranchName: backstage-integration
  rules:
    - allow: [Domain, Systems, Group, Component, System, API, Resource, Location]
  locations:
    # Local example data, file locations are relative to the backend process, typically `packages/backend`
    - type: file
      target: ../../examples/entities.yaml

    # Local example template
    - type: file
      target: ../../examples/template/template.yaml
      rules:
        - allow: [Template]

We can see a local example template and a rule, that from this location only Templates are allowed. Let’s have a look on the template itself:

apiVersion: t.backstage.io/v1beta3
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-template
kind: Template
metadata:
  name: example-nodejs-template
  title: Example Node.js Template
  description: An example template for the scaffolder that creates a simple Node.js service
spec:
  owner: user:guest
  type: service

  # These parameters are used to generate the input form in the frontend, and are
  # used to gather input data for the execution of the template.
  parameters:
    - title: Fill in some steps
      required:
        - name
      properties:
        name:
          title: Name
          type: string
          description: Unique name of the component
          ui:autofocus: true
          ui:options:
            rows: 5
    - title: Choose a location
      required:
        - repoUrl
      properties:
        repoUrl:
          title: Repository Location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com

  # These steps are executed in the scaffolder backend, using data that we gathered
  # via the parameters above.
  steps:
    # Each step executes an action, in this case one templates files into the working directory.
    - id: fetch-base
      name: Fetch Base
      action: fetch:template
      input:
        url: ./content
        values:
          name: ${{ parameters.name }}

    # This step publishes the contents of the working directory to GitHub.
    - id: publish
      name: Publish
      action: publish:github
      input:
        allowedHosts: ['github.com']
        description: This is ${{ parameters.name }}
        repoUrl: ${{ parameters.repoUrl }}

    # The final step is to register our new component in the catalog.
    - id: register
      name: Register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

  # Outputs are displayed to the user after a successful execution of the template.
  output:
    links:
      - title: Repository
        url: ${{ steps['publish'].output.remoteUrl }}
      - title: Open in catalog
        icon: catalog
        entityRef: ${{ steps['register'].output.entityRef }}

Let’s discuss this sample script in details:

Basically, the script will create a simple Node.js service.

The first section will provide some basic information:

The next part is the specification (spec), with the following information:

– owner: Specifies the owner of the template (`user:guest`).

– type: Defines the type of component this template creates (`service`).

Software Template Parameters

Now, lets talk about the parameters:

Parameters in a Backstage Software Template are used to gather necessary input data from the user through an interactive form in the frontend. These inputs are then utilized during the execution of the template. Let’s explain tat:

  1. Title: Each parameter group can have a title that describes what kind of inputs it will gather.
  2. Required Fields: A list of fields that are mandatory for the user to fill out.
  3. Properties: Defines the specifics of each input field, including:
  4. Title: The label shown to the user.
  5. Type: The data type of the input (e.g., string, number).
  6. Description: Additional information about the input.
  7. UI Options: Customizes the appearance and behavior of the input field (e.g., autofocus, rows for text fields).

After having discussed the input, let’s see what the template actually is doing. Software templates can be seen like workflows, that are executed sequentially.

Software Template Steps

Steps define the sequential actions executed in the scaffolder backend to automate the creation of software components. Here’s a concise overview:

  1. ID: A unique identifier for each step.
  2. Name: A human-readable name describing what the step does.
  3. Action: The specific action to be performed in the step. Actions can include tasks like fetching templates, publishing to repositories, or registering components.
  4. Input: Data required to perform the action. This can include parameters collected from the user or outputs from previous steps.

Example Breakdown

Step 1: Fetch Base Template

  • ID: fetch-base
  • Name: Fetch Base
  • Action: fetch:template
  • Input:
  • URL: Path to the template content (e.g., ./content).
  • Values: Variables to replace in the template (e.g., name parameter).

Step 2: Publish to GitHub

  • ID: publish
  • Name: Publish
  • Action: publish:github
  • Input:
  • Allowed Hosts: List of allowed repository hosts (e.g., github.com).
  • Description: Repository description using parameters.
  • RepoUrl: Repository URL provided by the user.

Step 3: Register Component

  • ID: register
  • Name: Register
  • Action: catalog:register
  • Input:
  • RepoContentsUrl: URL of the repository contents from the publish step.
  • CatalogInfoPath: Path to the catalog information file (e.g., /catalog-info.yaml).

Why do we need steps?

Steps in Backstage software templates are essential because they:

  • Define a clear sequence of automated tasks.
  • Allow the use of dynamic inputs and outputs to customize each action.
  • Ensure the template execution is systematic and reproducible.

In essence, steps guide the scaffolding process, making it possible to automate complex workflows and ensure consistency across different component creation tasks.

With that being said, let’s show the content which is used at the fetch-base step:


apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.name | dump }}
spec:
  type: service
  owner: user:guest
  lifecycle: experimental

In essence, the Scaffolder template replaces the expression ${{ values.name }} and hence creates a Component.

That’s enough for today. In the next blog post, we will show some advanced Scaffolder/ Software template stuff.

> Click here for Part 15: Backstage Software Templates II

Autor

Dr. Guido Söldner

Geschäftsführer

Guido Söldner ist Geschäftsführer und Principal Consultant bei Söldner Consult. Sein Themenfeld umfasst Cloud Infrastruktur, Automatisierung und DevOps, Kubernetes, Machine Learning und Enterprise Programmierung mit Spring.