Skip to main content
⏳ Estimated read time: 17 min read

StackGen Backstage scaffolder reference

StackGen Backstage scaffolder reference

Catalog of scaffolder actions (stackGen:*) and field extensions (ui:field) from
@stackgenhq/backstage-plugin-stackgen + @stackgenhq/backstage-plugin-stackgen-backend.


Wiring into Backstage

Selectors (frontend)

Import field extensions from @stackgenhq/backstage-plugin-stackgen and mount them under <ScaffolderFieldExtensions> (typically packages/app/src/App.tsx). Also register stackGenPlugin in createApp({ plugins: [...] }).

import {
TeamSelectFieldExtension,
AppStackNameFieldExtension,
TemplateAppStackSelectFieldExtension,
VaultSecretSelectFieldExtension,
ExporterGitConfigSelectFieldExtension,
ExporterBaseBranchFieldExtension,
ExporterPushBranchFieldExtension,
UserGroupSelectFieldExtension,
RoleSelectFieldExtension,
stackGenPlugin,
} from '@stackgenhq/backstage-plugin-stackgen';

// createApp({ plugins: [stackGenPlugin], ... })

<Route path="/create" element={<ScaffolderPage />}>
<ScaffolderFieldExtensions>
<TeamSelectFieldExtension />
<AppStackNameFieldExtension />
<TemplateAppStackSelectFieldExtension />
<VaultSecretSelectFieldExtension />
<ExporterGitConfigSelectFieldExtension />
<ExporterBaseBranchFieldExtension />
<ExporterPushBranchFieldExtension />
<UserGroupSelectFieldExtension />
<RoleSelectFieldExtension />
</ScaffolderFieldExtensions>
</Route>

Actions (backend)

In packages/backend/src/index.ts:

  1. Register actions via scaffolder.addActions(...).
  2. backend.add(import('@stackgenhq/backstage-plugin-stackgen-backend')) for the main plugin.
  3. backend.add(stackgenUiProxiesModule) so scaffolder fields can load data.
import {
createProjectAction,
createAppStackAction,
downloadIaCAction,
setStateBackendAction,
assignProjectMembersAction,
exportAppStackToGitAction,
stackgenUiProxiesModule,
} from '@stackgenhq/backstage-plugin-stackgen-backend';

scaffolder.addActions(
createProjectAction(config, logger),
createAppStackAction(config, logger),
downloadIaCAction(config, logger),
setStateBackendAction(config, logger),
assignProjectMembersAction(config, logger),
exportAppStackToGitAction(config, logger),
);

backend.add(import('@stackgenhq/backstage-plugin-stackgen-backend'));
backend.add(stackgenUiProxiesModule);

Field extensions (ui:field)

ui:fieldValue shapeui:options
TeamSelectstring (team/project Id)none
AppStackSelectstring (appstack name)teamId
TemplateAppStackSelectstring (template Id)none
VaultSecretSelectsecret identifier stringorgId / teamId, category, subcategory
UserGroupSelect{ type: 'user' or 'group', id, label? }[]groupFilterRegex, userFilterRegex, lockCurrentUser
RoleSelectstring (role Id)none
ExporterGitConfigSelectstring (config id)teamId / orgId, appstackName
ExporterBaseBranchstringteamId / orgId, configId
ExporterPushBranch{ branch?, useSameBranch? }teamId / orgId, appstackName

How ui:options resolve

Resolution styleFieldsBehavior
Form field or IdVaultSecretSelect, UserGroupSelectOption may name another form field, or be a literal Id. Waits until a form field has a value.
Form field name onlyAppStackSelect, ExporterGitConfigSelect, ExporterBaseBranch, ExporterPushBranchOption must be the name of another parameter; literal Ids are not supported.
No optionsTeamSelect, TemplateAppStackSelect, RoleSelect

orgId and teamId are aliases where both are read: if both are set, orgId is preferred.


What each field does with each option

TeamSelect

No ui:options. Loads allowed teams and returns the selected team Id.

AppStackSelect

OptionWhat it does
teamIdForm field name. When that field has a value, loads AppStacks for that team. When missing/empty, loads all AppStacks.

TemplateAppStackSelect

No ui:options. Loads template AppStacks.

VaultSecretSelect

OptionWhat it does
orgId / teamIdForm field name or Id. Omit both → enterprise (shared) secrets, no org filter.
categoryFilter by category
subcategoryFilter by subcategory

UserGroupSelect

OptionWhat it does
lockCurrentUserBoolean (default true). When true, forces token user into the selection, and prevents removing them.
userFilterRegexKeeps users whose email (fallback name) match. Invalid regex disables the field with an error.
groupFilterRegexKeeps groups whose name match. Invalid regex disables the field with an error.

RoleSelect

No ui:options. Static Admin / DevOps / Developer plus freeSolo custom Id.

LabelId
Admin00000000-0000-0000-0000-000000000002
DevOps00000000-0000-0000-0000-000000000003
Developer00000000-0000-0000-0000-000000000004

ExporterGitConfigSelect

OptionWhat it does
teamId / orgIdForm field name only. Resolved value sent as orgId to list configs. Required.
appstackNameForm field name only. Resolved value sent as appstackName query param. Required.

Waits until both team and appstack deps resolve. Auto-selects when exactly one config exists.

ExporterBaseBranch

OptionWhat it does
teamId / orgIdForm field name only → orgId when loading the selected exporter config.
configIdForm field name only. Loads that config and prefills from parameters.target_branch / targetBranch.

ExporterPushBranch

OptionWhat it does
teamId / orgIdForm field name only → orgId for export-history suggestion.
appstackNameForm field name only → appstackName for history lookup. Prefills branch from history

Example parameter blocks

teamId:
title: Team / Project
type: string
ui:field: TeamSelect

appstackName:
title: AppStack
type: string
ui:field: AppStackSelect
ui:options:
teamId: teamId

templateAppstackId:
title: Template AppStack
type: string
ui:field: TemplateAppStackSelect

members:
title: Members
type: array
ui:field: UserGroupSelect
ui:options:
lockCurrentUser: true
items:
type: object
required: [type, id]
properties:
type: { type: string, enum: [user, group] }
id: { type: string }
label: { type: string }

roleId:
title: Role
type: string
ui:field: RoleSelect
default: "00000000-0000-0000-0000-000000000004"

vaultSecret:
title: Vault secret
type: string
ui:field: VaultSecretSelect
ui:options:
teamId: teamId
category: scm
subcategory: credentials

existingExporterConfigId:
type: string
ui:field: ExporterGitConfigSelect
ui:options:
teamId: teamId
appstackName: appstackName

baseBranch:
type: string
ui:field: ExporterBaseBranch
ui:options:
teamId: teamId
configId: existingExporterConfigId

pushBranch:
type: object
ui:field: ExporterPushBranch
ui:options:
teamId: teamId
appstackName: appstackName
properties:
branch: { type: string }
useSameBranch: { type: boolean }

Actions (stackGen:*)

Action idFactory exportPurpose
stackGen:createProjectcreateProjectActionCreate project/git/env config
stackGen:createAppStackcreateAppStackActionCreate AppStack
stackGen:assignProjectMembersassignProjectMembersActionAssign users/groups + role
stackGen:setStateBackendsetStateBackendActionCreate/update env profile + state backend
stackGen:downloadIaCdownloadIaCActionDownload IaC ZIP into workspace
stackGen:exportToGitexportAppStackToGitActionExport via stackgen-exporter

stackGen:createProject

Creates a StackGen project with optional git and environment configuration. Idempotent on name conflict

Inputs (project)

FieldTypeRequiredDescription
project.namestringyesProject name
project.descriptionstringyesDescription
project.gitConfigobjectnoGit integration template
project.gitConfig.namestringnoConfig name
project.gitConfig.typestringnoe.g. git
project.gitConfig.parameters.scmTypeenumnoGITHUB
project.gitConfig.parameters.repoUrlstringnoRepo URL
project.gitConfig.parameters.targetBranchstringnoBranch
project.gitConfig.parameters.pathstringnoPath in repo
project.gitConfig.vaultReferencestringnoVault secret ref for SCM creds
project.gitConfig.defaultbooleannoMark as default
project.environmentConfigobjectnoEnv templates + variables
project.environmentConfig.namestringnoConfig name
project.environmentConfig.environmentTemplates[]objectno{ name, color, stateBackendTemplate? }
project.environmentConfig.variables[]objectno{ name, type, defaultValueJson, description }
project.environmentConfig.variableOverrides[]objectno{ variableName, environmentTemplateName, valueJson }
stateBackendTemplate.typeenumnoS3

Outputs

FieldTypeDescription
projectIdstringProject id
projectNamestringName
teamIdstringProject Id
alreadyExistedbooleanTrue if project already exists
projectURLstringProject URL
- id: create-project
name: Create StackGen Project
action: stackGen:createProject
input:
project:
name: ${{ parameters.projectName }}
description: ${{ parameters.projectDescription }}

stackGen:createAppStack

Creates an AppStack with explicit resources, or clones appstack template via templateAppstackId (mutually exclusive with a non-empty resources array).

Inputs (appstack)

FieldTypeRequiredDescription
appstack.name or appstackNamestringone requiredAppStack name
appstack.teamIdstringyesDestination team/project
appstack.cloudProviderstringif no templatee.g. aws, gcp, azure
appstack.templateAppstackIdIdnoClone from template AppStack
appstack.resources[]arraydefault []Resource objects

Resource object (simplified; full schema is on Installed actions in Backstage)

FieldNotes
resourceTypeRequired
resourcePackIdResource pack id
templateIdModules Id

Outputs

FieldTypeDescription
appstackIdstringAppStack id
appstackNamestringName
appStackURLstringStackGen UI URL
- id: create-appstack
action: stackGen:createAppStack
input:
appstack:
name: ${{ parameters.appstackName }}
teamId: ${{ steps['create-project'].output.teamId }}
cloudProvider: aws
resources: []

stackGen:assignProjectMembers

Assigns users/groups to a project with one role for all selected members.

Inputs

FieldTypeRequiredDescription
projectIdstringyesUse createProjectteamId
membersarrayno (default [])UserGroupSelect output
roleIdstringnoRole Id; defaults to Developer

Outputs

FieldTypeDescription
assignedUsers / assignedGroupsnumberCounts
assignedUsersText / assignedGroupsTextstringHelp text with users/groups added
roleIdstringRole applied
- id: assign-members
action: stackGen:assignProjectMembers
input:
projectId: ${{ steps['create-project'].output.teamId }}
members: ${{ parameters.members }}
roleId: ${{ parameters.roleId }}

stackGen:setStateBackend

Creates/updates an environment profile with a state backend (and optional TF variable definitions).

Inputs

FieldTypeRequiredDescription
teamIdstringyes
appstackNamestringyes
environmentNamestringyes
colorstringnodefault #000000
variables[]objectno{ name, type?, defaultValueJson?, description? }
variableValues[]{ name, value }noname/value pairs
stateBackend.typeenumyesS3
stateBackend.configRecord<string,string>yesBackend-specific keys

Outputs: success, environmentId?

- id: setBackendProd
action: stackGen:setStateBackend
input:
teamId: ${{ steps.createProject.output.teamId }}
topologyId: ${{ steps.createAppStack.output.topologyId }}
environmentName: production
color: "#FF0000"
variables:
- name: region
type: string
defaultValueJson: '"us-east-1"'
stateBackend:
type: S3
config:
bucket: my-tf-state
key: production
region: us-west-1
encrypt: "true"
dynamodb_table: TerraformLocks

stackGen:downloadIaC

Downloads IaC ZIP for a topology into the scaffolder workspace.

Inputs

FieldTypeRequired
teamIdstringyes
appstackNamestringyes
extractedIaCPathstringno (default ./)

Outputs: extractedIacPath

Often paired with publish:github:pull-request (Backstage built-in), not exportToGit.

- id: stackGenIaCDownload
action: stackGen:downloadIaC
input:
appstackName: ${{ parameters.name }}
teamId: ${{ parameters.teamId }}
extractedIaCPath: ${{ parameters.path }}

stackGen:exportToGit

Exports via stack-exporter: resolve topology, pick/bootstrap git config, open PR or commit.

Inputs

FieldTypeRequiredDescription
appstackNamestringyes
teamIdstringyes
appstackIdstringnoResolved if omitted
configIdstringnoExisting exporter config (wins over gitConfig)
gitConfigobjectnoBootstrap if no config; needs parameters.scmType, repoUrl, vaultReference
baseBranchstringnotarget_branch
pushBranchstringnobranch_format
useSameBranchbooleannoFrom ExporterPushBranch when suggestion kept
createPrbooleannofalse = commit only
overrides[]{ key, value }noe.g. pr_title, commit_msg

Outputs: pullRequestUrl?, externalLink?, committedBranch?, notes?

- id: exportToGit
action: stackGen:exportToGit
input:
teamId: ${{ parameters.teamId }}
appstackName: ${{ parameters.appstackName }}
configId: ${{ parameters.existingExporterConfigId }}
baseBranch: ${{ parameters.baseBranch }}
pushBranch: ${{ parameters.pushBranch.branch }}
useSameBranch: ${{ parameters.pushBranch.useSameBranch }}
createPr: ${{ parameters.createPr }}
overrides:
- key: pr_title
value: ${{ parameters.prTitle }}
- key: commit_msg
value: ${{ parameters.commitMessage }}

Example templates

1. Create a Basic project

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: stackgen-project-basic
title: Create Project (Basic)
description: Create an empty StackGen project
spec:
owner: user:guest
type: service
parameters:
- title: Project Information
required:
- projectName
properties:
projectName:
title: Project Name
type: string
ui:autofocus: true
steps:
- id: create-project
name: Create StackGen Project
action: stackGen:createProject
input:
project:
name: ${{ parameters.projectName }}
description: Project created via Backstage
output:
links:
- title: View Created Project
url: ${{ steps['create-project'].output.projectURL }}

2. Create a Project, assign members and create AppStack from template in the Project

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: stackgen-project-appstack-groups
title: Create Project, AppStack, and Groups
description: >
Create a StackGen project, provision an AppStack from a template, and
assign groups (or users) to the project.
tags:
- stackgen
- project
- infrastructure
- groups
spec:
owner: user:guest
type: service
parameters:
- title: Project
required:
- projectName
- members
properties:
projectName:
title: Project Name
type: string
description: Name of the StackGen project (team) to create
ui:autofocus: true
projectDescription:
title: Description
type: string
default: Project created via Backstage
members:
title: Groups / Members
type: array
description: >
Select one or more groups (and optional users) to associate with
the project.
ui:field: UserGroupSelect
ui:options:
lockCurrentUser: false
minItems: 1
items:
type: object
required:
- type
- id
properties:
type:
type: string
enum: [user, group]
id:
type: string
label:
type: string
roleId:
title: Project role
type: string
description: >
Role applied to every selected user/group. Pick Admin, DevOps, or
Developer. Leave blank to use the action default (developer).
ui:field: RoleSelect
default: "00000000-0000-0000-0000-000000000004"

- title: AppStack
required:
- appstackName
- templateAppstackId
properties:
appstackName:
title: AppStack Name
type: string
description: Name for the new AppStack in the created project
minLength: 4
cloudProvider:
title: Cloud Provider (optional)
type: string
description: Overrides provider from the template when set
enum:
- aws
- gcp
- azure
templateAppstackId:
title: Template AppStack
type: string
description: StackGen template AppStack whose topology is cloned
ui:field: TemplateAppStackSelect

steps:
- id: create-project
name: Create StackGen Project
action: stackGen:createProject
input:
project:
name: ${{ parameters.projectName }}
description: ${{ parameters.projectDescription }}

- id: create-appstack
name: Create AppStack from Template
action: stackGen:createAppStack
input:
appstack:
name: ${{ parameters.appstackName }}
teamId: ${{ steps['create-project'].output.teamId }}
templateAppstackId: ${{ parameters.templateAppstackId }}
cloudProvider: ${{ parameters.cloudProvider }}
resources: []

- id: assign-groups
name: Assign Groups to Project
action: stackGen:assignProjectMembers
input:
projectId: ${{ steps['create-project'].output.teamId }}
members: ${{ parameters.members }}
roleId: ${{ parameters.roleId }}

output:
links:
- title: View Created Project
url: ${{ steps['create-project'].output.projectURL }}
- title: View Created AppStack in StackGen
url: ${{ steps['create-appstack'].output.appStackURL }}
text:
- title: Assigned groups
content: ${{ steps['assign-groups'].output.assignedGroupsText }}
- title: Assigned users
content: ${{ steps['assign-groups'].output.assignedUsersText }}
- title: Role used
content: ${{ steps['assign-groups'].output.roleId }}

3. Export to Git (existing exporter config)

Uses stackGen:exportToGit with TeamSelect, AppStackSelect, and the Exporter* fields. If no config exists and configId is blank, the action bootstraps from the project default git template.

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: stackgen-git-export-exporter
title: Push IaC via StackGen exporter
description: >
Export an existing AppStack to Git via stack-exporter. Pick team and AppStack;
topology and exporter appstackId are resolved when omitted.
spec:
owner: user:guest
type: service
parameters:
- title: Stack context
required:
- teamId
- appstackName
properties:
teamId:
type: string
title: Team / project
ui:field: TeamSelect
appstackName:
type: string
title: AppStack
ui:field: AppStackSelect
ui:options:
teamId: teamId
existingExporterConfigId:
type: string
title: Existing exporter config
description: >
Optional. Auto-selected when there is only one config. Leave blank to
create from the project default if none exist.
ui:field: ExporterGitConfigSelect
ui:options:
teamId: teamId
appstackName: appstackName

- title: Git export options
properties:
baseBranch:
type: string
title: Base branch
description: Prefills from the selected config target branch.
ui:field: ExporterBaseBranch
ui:options:
teamId: teamId
configId: existingExporterConfigId
pushBranch:
type: object
title: Push branch
description: >
Prefills from export history. Leaving the suggestion unchanged
reuses that branch (useSameBranch).
ui:field: ExporterPushBranch
ui:options:
teamId: teamId
appstackName: appstackName
properties:
branch:
type: string
useSameBranch:
type: boolean
createPr:
type: boolean
title: Create pull request
description: >
When on, opens a PR against the base branch. When off, commits
directly without a pull request.
default: true

- title: Pull request
required:
- prTitle
- commitMessage
properties:
prTitle:
type: string
title: Pull request title
default: StackGen IaC export
commitMessage:
type: string
title: Commit message
default: Export IaC from StackGen

steps:
- id: exportToGit
name: Export AppStack to Git
action: stackGen:exportToGit
input:
teamId: ${{ parameters.teamId }}
appstackName: ${{ parameters.appstackName }}
configId: ${{ parameters.existingExporterConfigId }}
baseBranch: ${{ parameters.baseBranch }}
pushBranch: ${{ parameters.pushBranch.branch }}
useSameBranch: ${{ parameters.pushBranch.useSameBranch }}
createPr: ${{ parameters.createPr }}
overrides:
- key: pr_title
value: ${{ parameters.prTitle }}
- key: commit_msg
value: ${{ parameters.commitMessage }}

output:
links:
- title: View pull request
url: ${{ steps.exportToGit.output.pullRequestUrl }}
text:
- title: Export result
content: ${{ steps.exportToGit.output.notes }}

4. Export to Git (bootstrap gitConfig)

Same action, but creates an appstack-scoped git exporter config when you pass gitConfig (SCM type, repo URL, vault credential). configId still wins when set.

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: stackgen-git-export-with-gitconfig
title: Push IaC via StackGen exporter (with gitConfig)
description: >
Export an AppStack to Git. Use an existing exporter config, or create one
via gitConfig fields (VaultSecretSelect for SCM credentials).
spec:
owner: user:guest
type: service
parameters:
- title: Stack context
required:
- teamId
- appstackName
- exporterConfigMode
properties:
teamId:
type: string
title: Team / project
ui:field: TeamSelect
appstackName:
type: string
title: AppStack
ui:field: AppStackSelect
ui:options:
teamId: teamId
exporterConfigMode:
type: string
title: Git exporter config
enum:
- existing
- create
enumNames:
- Use existing
- Create new
default: existing
dependencies:
exporterConfigMode:
oneOf:
- properties:
exporterConfigMode:
const: existing
existingExporterConfigId:
type: string
title: Existing exporter config
ui:field: ExporterGitConfigSelect
ui:options:
teamId: teamId
appstackName: appstackName
- required:
- scmType
- repoUrl
- vaultReference
properties:
exporterConfigMode:
const: create
gitConfigName:
type: string
title: Config name
default: default-git-config
scmType:
type: string
title: SCM type
enum:
- GITHUB
- GITLAB
- BITBUCKET
- AZUREDEV
default: GITHUB
repoUrl:
type: string
title: Repository URL
description: e.g. https://github.com/org/repo.git
targetBranch:
type: string
title: Default target branch
default: main
path:
type: string
title: Path in repository
default: /
vaultReference:
type: string
title: SCM credential (vault secret)
ui:field: VaultSecretSelect
ui:options:
teamId: teamId
category: scm

- title: Git export options
properties:
baseBranch:
type: string
title: Base branch
ui:field: ExporterBaseBranch
ui:options:
teamId: teamId
configId: existingExporterConfigId
pushBranch:
type: object
title: Push branch
ui:field: ExporterPushBranch
ui:options:
teamId: teamId
appstackName: appstackName
properties:
branch:
type: string
useSameBranch:
type: boolean
createPr:
type: boolean
title: Create pull request
default: true

- title: Pull request
required:
- prTitle
- commitMessage
properties:
prTitle:
type: string
title: Pull request title
default: StackGen IaC export
commitMessage:
type: string
title: Commit message
default: Export IaC from StackGen

steps:
- id: exportToGit
name: Export AppStack to Git
action: stackGen:exportToGit
input:
teamId: ${{ parameters.teamId }}
appstackName: ${{ parameters.appstackName }}
configId: ${{ parameters.existingExporterConfigId }}
gitConfig:
name: ${{ parameters.gitConfigName }}
type: git
parameters:
scmType: ${{ parameters.scmType }}
repoUrl: ${{ parameters.repoUrl }}
targetBranch: ${{ parameters.targetBranch }}
path: ${{ parameters.path }}
vaultReference: ${{ parameters.vaultReference }}
default: true
baseBranch: ${{ parameters.baseBranch }}
pushBranch: ${{ parameters.pushBranch.branch }}
useSameBranch: ${{ parameters.pushBranch.useSameBranch }}
createPr: ${{ parameters.createPr }}
overrides:
- key: pr_title
value: ${{ parameters.prTitle }}
- key: commit_msg
value: ${{ parameters.commitMessage }}

output:
links:
- title: View pull request
url: ${{ steps.exportToGit.output.pullRequestUrl }}
text:
- title: Export result
content: ${{ steps.exportToGit.output.notes }}

5. Advanced project (git + env templates on create)

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: stackgen-project-advanced
title: Create Project (Advanced)
description: Create a project with Git integration, environments, variables, and overrides
spec:
owner: user:guest
type: service
parameters:
- title: Project Information
required:
- projectName
- repoUrl
properties:
projectName:
title: Project Name
type: string
ui:autofocus: true
repoUrl:
title: Repository URL
type: string
default: https://github.com/org/repo.git

steps:
- id: create-project
name: Create StackGen Project
action: stackGen:createProject
input:
project:
name: ${{ parameters.projectName }}
description: Project created via Backstage with Advanced Config
gitConfig:
name: default-git-config
type: git
parameters:
scmType: GITHUB
repoUrl: ${{ parameters.repoUrl }}
targetBranch: main
default: true
environmentConfig:
name: default-env-config
environmentTemplates:
- name: dev
color: "#1cbb9b"
- name: production
color: "#FF0000"
variables:
- name: region
type: string
defaultValueJson: '"us-east-1"'
description: AWS region for deployment
variableOverrides:
- variableName: region
environmentTemplateName: production
valueJson: '"us-west-2"'

output:
links:
- title: View Created Project
url: ${{ steps['create-project'].output.projectURL }}