Scaffolder .NET Actions adds dotnet tooling to your Backstage scaffolder. It exposes template actions that wrap the dotnet CLI so your templates can spin up new .NET projects during a scaffolding run. You keep the flow inside Backstage. No context switching.
At its core it provides a dotnet new action. A template step can call dotnet new to create a web API or another project type from the standard templates. Recent community releases ship this through the Backstage community plugins scope.
This is useful if you run .NET in production and want paved roads in your portal. Platform teams can give engineers an easy way to create a service with the right structure, naming, and defaults. A single template can create the repo, scaffold the .NET project, publish to your git provider, then register in the catalog. You can standardize the first mile for APIs, web apps, or Aspire based projects while keeping the template readable and versioned in git.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the package
Run this in the repo root
yarn workspace backend add <the plugin package name from the registry>
If the plugin ships any frontend package run this too
yarn workspace app add <the frontend package name if present>
Replace the placeholders with the exact package names from the plugin page
Wire it into the new backend system
If your backend uses the new backend system with createBackend do this
Add the module to packages backend src index ts
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// The plugin module usually exports a function you can add to the backend
// Replace the import path and the exported symbol with the ones from the plugin
backend.add(
import('<the plugin package name>').then(m =>
// common export names look like scaffolderModuleDotnet or dotnetModule
// check the plugin for the exact function
m.scaffolderModuleDotnet()
),
);
backend.start();
If the module needs config add that under app config yaml as the plugin docs describe
Wire it into the old backend system
If your backend still uses the old createRouter pattern hook the actions into the scaffolder backend
Add imports in packages backend src plugins scaffolder ts
// packages/backend/src/plugins/scaffolder.ts
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { createRouter } from '@backstage/plugin-scaffolder-backend';
// Replace the import path with the plugin package
// Replace the imported functions with the ones the plugin exports
import {
createDotnetNewAction,
createDotnetRestoreAction,
createDotnetBuildAction,
createDotnetTestAction,
createDotnetPublishAction,
createNugetPushAction,
} from '<the plugin package name>';
export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
const actions = [
createDotnetNewAction(),
createDotnetRestoreAction(),
createDotnetBuildAction(),
createDotnetTestAction(),
createDotnetPublishAction(),
createNugetPushAction(),
];
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
catalogClient: env.catalogClient,
// register the custom actions
actions,
});
}
Make sure packages backend src index ts mounts the scaffolder router
// packages/backend/src/index.ts
import scaffolder from './plugins/scaffolder';
// inside main bootstrap where apiRouter is created
const scaffolderRouter = await scaffolder(env);
apiRouter.use('/scaffolder', scaffolderRouter);
If the plugin exposes any other backend helpers import and wire them as the docs show
Make the actions visible in your Scaffolder UI
This plugin is backend only. You do not add any new visual components from it. Your users will interact with the actions through templates
Ensure the Scaffolder page is routed in the frontend
// packages/app/src/App.tsx
import React from 'react';
import { Route } from 'react-router';
import { FlatRoutes } from '@backstage/core-app-api';
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
// inside your app routes
export const AppRoutes = () => (
<FlatRoutes>
<Route path="/create" element={<ScaffolderPage />} />
{/* other routes */}
</FlatRoutes>
);
Add a sidebar item so users can find it
// packages/app/src/components/Root/Root.tsx
import React from 'react';
import {
Sidebar,
SidebarItem,
SidebarDivider,
} from '@backstage/core-components';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
export const Root = ({ children }: { children?: React.ReactNode }) => (
<>
<Sidebar>
{/* other items */}
<SidebarDivider />
<SidebarItem to="/create" text="Create" icon={CreateComponentIcon} />
</Sidebar>
{children}
</>
);
Add a template that uses the dotnet actions
Create a template file in your catalog. For example catalog templates dotnet starter template yaml
Below is an example. Replace action ids and input keys with the ones documented by the plugin
# catalog/templates/dotnet-starter/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: dotnet-starter
title: New dotnet service
description: Create a dotnet service from a template
spec:
owner: team-a
type: service
parameters:
- title: Service info
required:
- name
properties:
name:
type: string
title: Service name
solutionName:
type: string
title: Solution name
templateShortName:
type: string
title: dotnet template short name
default: webapi
steps:
- id: dotnet-new
name: Create project
action: dotnet:new
input:
workingDirectory: .
template: '${{ parameters.templateShortName }}'
name: '${{ parameters.name }}'
output: '${{ parameters.solutionName }}'
- id: dotnet-restore
name: Restore
action: dotnet:restore
input:
workingDirectory: '${{ steps.dotnet-new.output.path }}'
- id: dotnet-build
name: Build
action: dotnet:build
input:
workingDirectory: '${{ steps.dotnet-new.output.path }}'
configuration: Release
- id: dotnet-test
name: Test
action: dotnet:test
input:
workingDirectory: '${{ steps.dotnet-new.output.path }}'
- id: dotnet-publish
name: Publish
action: dotnet:publish
input:
workingDirectory: '${{ steps.dotnet-new.output.path }}'
configuration: Release
output: dist
# If you plan to push to a package feed
# - id: nuget-push
# name: Push package
# action: nuget:push
# input:
# packagePath: 'dist/*.nupkg'
# source: 'https://your-nuget-source'
# apiKey: '${{ secrets.nugetApiKey }}'
output:
links:
- title: Open the new project
url: '${{ steps.dotnet-new.output.path }}'
Register the template location in your app config if you are not already loading templates from that folder
# app-config.yaml
catalog:
locations:
- type: file
target: catalog/templates/**/*.yaml
rules:
- allow: [Template]
Notes on action ids and exports
Action ids like dotnet new or dotnet restore vary by plugin. The code above uses common ids as placeholders. Use the exact ids listed by the plugin
For the new backend system the module function name can differ. Common names look like scaffolderModuleDotnet. Use the exact export from the plugin
For the old backend system the action factory function names can differ. Use the exact names from the plugin and add them to the actions array as shown above
Build and run
Install workspace deps
yarn install
Start backend and app in separate terminals
yarn workspace backend start
yarn workspace app start
Changelog
The Scaffolder .NET Actions plugin has not seen any significant changes since a year ago.
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.