Service Onboarding Automation

Service onboarding automation turns a developer’s “I need a new service” — or a new hire’s first day — into a deterministic, auditable sequence that ends with a productive, cataloged result instead of a week of manual ticket-chasing.

This sub-section of Developer Experience & Self-Service Platforms covers the orchestration layer: chaining repository scaffolding, CI bootstrap, infrastructure provisioning, catalog registration, and access grants into a single self-service flow. The automation engine is the Backstage scaffolder, and the action sequencing builds directly on Scaffolder Template Design.

Onboarding pipeline from a portal form to a registered service with access granted A portal intake form triggers repository creation, CI bootstrap, catalog registration, and an access grant, each step passing outputs to the next. Intake Form parameters Create Repo publish:github Bootstrap CI + IaC Register catalog entity Grant access
Each onboarding step consumes the previous step's output, producing a fully wired service.

Prerequisites & Environment Baseline

  • Scaffolder backend: @backstage/plugin-scaffolder-backend@^1.22.0 plus @backstage/plugin-scaffolder-backend-module-github@^0.5.0 for repository actions.
  • Provider credentials via env: ${GITHUB_TOKEN} (repo + workflow scopes), ${ARGOCD_AUTH_TOKEN} for deployment registration, and ${VAULT_TOKEN} for secret namespace creation. Inject these through app-config.yaml, never inline.
  • Catalog with group ownership: Access grants resolve teams from Group entities; ensure your identity provider sync populates them.
  • An RBAC policy: Decide which groups may run the onboarding template, consistent with your Team Permission Models.
  • CLI toolchain: @backstage/cli@^0.27.0 for dry-run and validation.

Step-by-Step Configuration & Plugin Architecture

1. Capture intent in a minimal form

The intake form should collect only what cannot be defaulted. Everything else is supplied by the golden path.

# templates/onboarding.yaml
# Requires scaffolder.backstage.io/v1beta3 (Backstage >= 1.22.0)
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: service-onboarding
  title: Onboard a New Service
spec:
  owner: group:platform-engineering
  type: service
  parameters:
    - title: Service basics
      required: [name, owner, system]
      properties:
        name: { title: Service name, type: string, pattern: "^[a-z][a-z0-9-]{2,38}$" }
        owner:
          title: Owning team
          type: string
          ui:field: OwnerPicker
          ui:options: { catalogFilter: { kind: Group } }
        system: { title: Parent system, type: string, ui:field: EntityPicker }

2. Chain the provisioning steps

Sequence actions so each consumes the prior step’s output. Repository scaffolding is covered in depth in Automating Repository Scaffolding with Backstage Software Templates.

  steps:
    - id: scaffold
      name: Render service files
      action: fetch:template
      input:
        url: ./skeleton
        values: { name: ${{ parameters.name }}, owner: ${{ parameters.owner }} }

    - id: publish
      name: Create repository
      action: publish:github
      input:
        repoUrl: github.com?owner=${GITHUB_ORG}&repo=${{ parameters.name }}
        defaultBranch: main
        protectDefaultBranch: true

    - id: register
      name: Register in catalog
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

3. Separate human onboarding from service onboarding

People onboarding reuses the same engine but provisions accounts, group memberships, and access rather than repositories — covered in Onboarding New Engineers with Self-Service Workflows.

Validation & Health Checks

# Validate and dry-run the onboarding template
# Requires @backstage/cli >= 0.27.0
set -euo pipefail
npx @backstage/cli catalog validate --path ./templates/onboarding.yaml
# expected: "Validated 1 entity ... 0 errors"

After a dry-run, confirm the generated entity resolves an owner:

yq '.spec.owner' ./dry-run-output/catalog-info.yaml | grep -qE '^group:' && echo "owner resolved"
# expected: "owner resolved"

Maintenance & Lifecycle Management

  • Upgrade path: Pin provider modules and bump them in lockstep with provider API deprecations; run the nightly end-to-end onboarding smoke test against a throwaway org before promoting a new module version.
  • Rollback: On a failed onboarding run, the scaffolder leaves a partial repository. Provide a cleanup task (gh repo delete, catalog entity removal) and make onboarding steps idempotent where possible.
  • Debug commands: LOG_LEVEL=debug surfaces each action’s input and output, which is the fastest way to find where a chain breaks.
  • Metrics: Track time-to-first-commit and onboarding success rate via Developer Experience Metrics.

Common Pitfalls & Mitigation Strategies

  • Non-idempotent steps. Root cause: actions that fail if the repository already exists. Fix: guard with existence checks and make retries safe so a half-failed run can be re-run.
  • Synchronous human approvals. Root cause: a manager-approval gate embedded mid-flow. Fix: move approvals to asynchronous policy and notify, rather than blocking the pipeline.
  • Broad service tokens. Root cause: one powerful token for all actions. Fix: scope tokens per action and pass the requesting user’s identity where supported.
  • Orphaned catalog entities. Root cause: registration succeeding after a later step fails. Fix: register last, or reconcile periodically against the source repository.

Frequently Asked Questions

Should onboarding provision infrastructure directly or open a request to a platform pipeline?

For low-risk, well-bounded resources (a repository, a CI pipeline, a namespace) provision directly within the onboarding flow so the developer gets a complete result. For high-blast-radius resources (production databases, networking) emit a declarative request that a separate platform pipeline reconciles, keeping ownership of risky changes with the platform while still giving the developer a single self-service entry point.

How do we keep onboarding flows from drifting as provider APIs change?

Pin every provider module to an exact version, run a scheduled end-to-end smoke test against a disposable target, and treat the onboarding template like any other production service with its own CI and on-call. Drift surfaces in the smoke test long before a real developer hits it.

Can the same flow onboard both services and engineers?

Use one engine, two templates. Service onboarding produces repositories and catalog entities; engineer onboarding produces accounts, group memberships, and access grants. Sharing the scaffolder keeps the execution surface, audit trail, and RBAC model consistent across both.