Create PagerDuty service with Software Templates
The PagerDuty backend plugin provides a custom action that you can use in your Backstage Software Templates to create services in PagerDuty easily. This custom action not only creates the service, but also automatically configures a Backstage integration and adds its integration key to the Backstage Service configuration.
By doing so, it enables and configures the PagerDuty Card provided by the frontend plugin directly on your service page, eliminating the need for manual configuration and enhancing the user experience.
Adding the Scaffolder Actions plugin
Note
Version 0.6.0 of @pagerduty/backstage-plugin-backend
introduced support for the Backstage's new backend system which forced the extraction of the scaffolder actions to a separate package (@pagerduty/backstage-plugin-scaffolder-actions).
If you were already using the scaffolder actions before this, follow the migration guide here as you need to update the package used in the code.
- This command adds
@pagerduty/backstage-plugin-scaffolder
package to thepackages/backend
folder because it is a backend module.
Adding the custom action to the project
You can add the custom action to the project in two different ways. Using the legacy backend system or the new backend system. Follow one of the approaches detailed below.
Legacy backend system
Backstage Scaffolder capabilities can be extended with custom actions that support Software Templates. For that to happen you need to update packages/backend/src/plugins/scaffolder.ts
and add custom actions.
Now, the list of scaffolder actions cannot be appended so you need re-create it and append your custom action. Otherwise all actions will be replaced just with yours.
// Add imports
import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend';
import { ScmIntegrations } from '@backstage/integration';
import { createPagerDutyServiceAction } from '@pagerduty/backstage-plugin-scaffolder-actions';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
...
// Pull integrations
const integrations = ScmIntegrations.fromConfig(env.config);
// Rebuild built-in actions
const builtInActions = createBuiltinActions({
integrations,
catalogClient,
config: env.config,
reader: env.reader,
});
// Append PagerDuty custom action to the list
const actions = [
...builtInActions,
createPagerDutyServiceAction()
];
// Add new action list to the scaffolder
return await createRouter({
actions, // this is the only update needed on this list
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
catalogClient,
identity: env.identity,
permissions: env.permissions,
});
}
This step registers the custom action with the Scaffolder and allows it to be used in the Software template which you will configure on next step.
New backend system
Backstage's new backend system simplifies the configuration of backend plugins and requires less code to setup plugins. To add the PagerDuty scaffolder actions to your Backstage application add the following in packages/backend/src/index.ts
.
Adding the custom action to a Software Template
Software Templates can be simple or complex depending on the practices you are trying to standardize across your teams. Here, we provide a simple example of a template that requests basic information for a Backstage service and augments that with information relevant for creating the service in PagerDuty.
Note
We assume you have an examples/template/content
folder which is automatically create when you use npx @backstage/create-app
to create your Backstage project.
Create project configuration file
In your examples/template/content
folder you should create catalog-info.yaml
file if you don't have one already. Update its content to something like this:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{ values.name | dump }}
annotations:
pagerduty.com/integration-key: ${{ values.integrationKey | dump }}
pagerduty.com/service-id: ${{ values.serviceId | dump }}
pagerduty.com/account: ${{ values.account | dump }}
spec:
type: website
lifecycle: experimental
owner: guests
In the software template we will either replace the values for the integration-key
, service-id
, account
and name
or completely remove the parameters from the configuration.
Note
You don't need to use this exact template but to automate the configuration for the PagerDuty Card you need to have at least the integration-key
or the service-id
parameters.
Create the Software Template
Now that we have defined the contents of the new project we will be creating through a Software Template, we need to create the template itself.
Software templates are composed of input parameters, steps and outputs. In the following template we provide the user with two pages:
- Collecting information about the service itself
- Collecting information on where the code is going to be hosted
Once all the information is provided by the user we will:
- create a Service in PagerDuty
- parse the
catalog-info.yaml
file and replace the variables with the values generated by the previous step - publish the code to GitHub
- register the component in Backstage
Note
For the following template to work, you need to configure the apiToken
in app-config.yaml
file. If you haven't done so, follow the steps in Configure Backend plugin API credentials
Note: If you don't setup this property in your configuration, the backend plugin will fail to start.
Note
The UI component that allows users to select the Escalation Policy when creating a new service in PagerDuty depends on an open source component. For the template to work properly please install it before.
Once it is installed, go to /packages/app/src/App.tsx
and find <ScaffolderPage />
. Add the UI component to ScaffolderFieldExtensions
. It should look like this when you finish.
import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react';
import { SelectFieldFromApiExtension } from '@roadiehq/plugin-scaffolder-frontend-module-http-request-field';
...
<Route path="/create" element={<ScaffolderPage />}>
<ScaffolderFieldExtensions>
<SelectFieldFromApiExtension />
</ScaffolderFieldExtensions>
</Route>
Once all requirements are in-place, create a template.yaml
file under examples/template
and copy the following code in there.
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: create-pagerduty-service
title: Create PagerDuty Service
description: Creates service in PagerDuty
spec:
owner: pagerduty
type: service
parameters:
- title: PagerDuty Service
required:
- service_name
- description
- escalation_policy_id
properties:
service_name:
title: Service Name
type: string
description: The name of the service
description:
title: Description
type: string
description: The description of the service
escalation_policy_id:
title: Escalation Policy ID
type: string
description: The ID of the escalation policy to associate with the service
ui:field: SelectFieldFromApi #(1)!
ui:options: #(2)!
title: PagerDuty Escalation Policy
description: Select an escalation policy from PagerDuty
path: 'pagerduty/escalation_policies' #(3)!
labelSelector: 'label'
valueSelector: 'value'
placeholder: '---'
alert_grouping: #(4)!
title: Alert Grouping
type: string
description: Reduce noise by grouping similar alerts - Defaults to 'None'.
enum:
- 'time'
- 'intelligent'
- 'content_based'
enumNames:
- 'Time-based grouping'
- 'Intelligent grouping'
- 'Content-based grouping'
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
steps:
- id: pagerdutyService
name: Create PagerDuty Service
action: pagerduty:service:create
input:
name: ${{ parameters.service_name }}
description: ${{ parameters.description }}
escalationPolicyId: ${{ parameters.escalation_policy_id }}
alertGrouping: ${{ parameters.alert_grouping }} #(5)!
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./content
templateFileExtension: '.yaml'
values:
name: ${{ parameters.service_name }}
serviceId: ${{ steps['pagerdutyService'].output.serviceId }}
integrationKey: ${{ steps['pagerdutyService'].output.integrationKey }}
account: ${{ steps['pagerdutyService'].output.account }}
- id: publish
name: Publish
action: publish:github
input:
allowedHosts: ['github.com']
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info'
output:
links:
- title: Open in PagerDuty
url: ${{ steps['pagerdutyService'].output.serviceUrl }}
text:
- title: Integration Key
text: ${{ steps['pagerdutyService'].output.integrationKey }}
- Open source dropdown component from
@roadiehq
that queries data from a local API - Options for the dropdown component
- The local api exposed by the PagerDuty backend plugin that retrieves a list of key/value pairs
- This UI field is optional. If you want to enforce a specific method you can just set the value in the backend component.
- This parameter is optional. You can enforce a specific method by choosing 'intelligent', 'time' or 'content_based'. If not defined, no alert grouping will be configured. Requires AIOps.
This is an easy mechanism for onboarding new services in an automated way, ensuring that Backstage and PagerDuty services can be provisioned with one step and in a self-service way.