Onboarding New Engineers with Self-Service Workflows
This how-to applies the scaffolder to people rather than services: a self-service workflow that provisions a new engineer’s group memberships, repository access, and starter tasks on day one, with every grant attributed and auditable. It is part of Service Onboarding Automation within Developer Experience & Self-Service Platforms, and the access grants must conform to your Team Permission Models.
Prerequisites
@backstage/plugin-scaffolder-backend@^1.22.0with a custom action module for identity-provider grants.${IDP_API_TOKEN}for the identity provider (group membership) and${GITHUB_TOKEN}for repository team membership, injected viaapp-config.yaml.- Catalog
GroupandUserentities synced from the identity provider so the workflow can resolve teams. - A manager or buddy
Userto receive the asynchronous approval notification. @backstage/cli@^0.27.0for validation and dry-run.
Exact Configuration
1. Capture the new-hire intake
# templates/onboard-engineer.yaml
# Requires scaffolder.backstage.io/v1beta3 (Backstage >= 1.22.0)
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: onboard-engineer
title: Onboard a New Engineer
spec:
owner: group:people-ops
type: onboarding
parameters:
- title: New engineer
required: [userRef, team, startDate]
properties:
userRef:
title: Engineer
type: string
ui:field: EntityPicker
ui:options: { catalogFilter: { kind: User } }
team:
title: Joining team
type: string
ui:field: OwnerPicker
ui:options: { catalogFilter: { kind: Group } }
startDate: { title: Start date, type: string, format: date }
2. Grant group membership through a custom action
Repository and service-creation actions ship with Backstage; identity-provider grants are organization-specific, so register a custom action (see the backend-plugin patterns in Building Custom Backstage Plugins).
// src/scaffolder/actions/grantGroupMembership.ts
// Requires @backstage/plugin-scaffolder-node >= 0.4.0
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
export const grantGroupMembership = createTemplateAction<{
userRef: string;
team: string;
}>({
id: 'idp:group:grant',
schema: {
input: {
type: 'object',
required: ['userRef', 'team'],
properties: {
userRef: { type: 'string' },
team: { type: 'string' },
},
},
},
async handler(ctx) {
const { userRef, team } = ctx.input;
// Token injected at runtime; never hardcode
const res = await fetch(`${process.env.IDP_API_URL}/groups/${team}/members`, {
method: 'POST',
headers: { authorization: `Bearer ${process.env.IDP_API_TOKEN}` },
body: JSON.stringify({ user: userRef }),
});
if (!res.ok) throw new Error(`grant failed: ${res.status}`);
ctx.logger.info(`granted ${userRef} membership in ${team}`);
},
});
3. Sequence the workflow with asynchronous approval
Keep human review out of the critical path: provision the safe grants immediately, and emit an approval request for anything that needs sign-off rather than blocking the run.
steps:
- id: grant-team
name: Add to team group
action: idp:group:grant
input:
userRef: ${{ parameters.userRef }}
team: ${{ parameters.team }}
- id: starter-repo-access
name: Grant starter repository access
action: github:repo:collaborator:add
input:
repoUrl: github.com?owner=${GITHUB_ORG}&repo=onboarding-tasks
username: ${{ parameters.userRef }}
permission: push
- id: notify-buddy
name: Notify onboarding buddy
action: http:backstage:request
input:
method: POST
path: /api/notifications/onboarding
body:
userRef: ${{ parameters.userRef }}
startDate: ${{ parameters.startDate }}
4. Register the custom action
// src/scaffolder/index.ts
// Requires @backstage/plugin-scaffolder-backend >= 1.22.0
import { grantGroupMembership } from './actions/grantGroupMembership';
export const additionalActions = [grantGroupMembership()];
Validation
# Requires @backstage/cli >= 0.27.0
set -euo pipefail
# 1. Validate the template
npx @backstage/cli catalog validate --path ./templates/onboard-engineer.yaml
# expected: "Validated 1 entity ... 0 errors"
# 2. Unit-test the custom action against a mocked IdP
npm run test -- grantGroupMembership
# expected: "PASS ... grants membership"
# 3. Dry-run resolves the user and team to entity refs
yq '.spec.steps[0].input.userRef' ./dry-run-output/run.yaml | grep -qE '^user:'
# expected: exit 0
Edge Cases & Troubleshooting
| Symptom | Root Cause | Resolution |
|---|---|---|
| Grant succeeds but membership absent | Identity-provider sync lags the grant | Trigger a catalog refresh after the grant, or poll the IdP before marking the step done |
| Workflow blocks on approval | Human gate placed in the critical path | Move approval to the asynchronous notify step; provision safe grants immediately |
| Re-running duplicates memberships | Action not idempotent | Treat 409 Conflict from the IdP as success in the action handler |
EntityPicker shows no users |
User entities not synced from the IdP |
Verify the org-data ingestion is running and User kind is allowed |
| Audit log missing the actor | Action ran under a shared token | Forward the requesting user’s identity into the action and log it on every grant |
Frequently Asked Questions
Should engineer onboarding live in the same portal as service onboarding?
Yes — sharing one scaffolder gives both flows a single audit trail, a consistent RBAC model, and one place developers and people-ops look. The templates differ (people produce access grants, services produce repositories) but the execution surface, permission checks, and observability are identical, which is exactly the consistency you want for anything that grants access.
How do I keep access grants least-privilege during onboarding?
Grant only the memberships the joining team’s role requires and let downstream RBAC derive everything else from group membership, rather than enumerating per-repository permissions in the workflow. This keeps the onboarding template stable as projects change and concentrates authorization logic in your permission policy where it belongs.
Related
- Service Onboarding Automation — parent sub-section
- Developer Experience & Self-Service Platforms — section overview
- Automating Repository Scaffolding with Backstage Software Templates — the service-onboarding counterpart
- Team Permission Models — the access model these grants follow