From 25ca8ff4ee46ec9a541de6075ece9ce5ceaa90db Mon Sep 17 00:00:00 2001 From: synergy0225 Date: Wed, 22 Jul 2026 15:24:58 -0400 Subject: [PATCH 1/3] Create README.md --- .../PagerDuty/README.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 custom_integration_mapping/PagerDuty/README.md diff --git a/custom_integration_mapping/PagerDuty/README.md b/custom_integration_mapping/PagerDuty/README.md new file mode 100644 index 0000000..13934c6 --- /dev/null +++ b/custom_integration_mapping/PagerDuty/README.md @@ -0,0 +1,116 @@ +# PagerDuty custom integration setup: Pull the service catalog from PagerDuty into your OpsLevel catalog + +This README provides a step-by-step guide to setting up a custom integration mapping in OpsLevel to import PagerDuty Service IDs into your OpsLevel catalog, enabling Custom Actions (e.g. triggering PagerDuty incidents) without manually tagging each service. + +## Overview of Custom Integrations + +OpsLevel's custom integration system supports two patterns: +* **Push Integrations**: Where external systems send data directly to OpsLevel via webhooks. +* **Pull Integrations**: Where OpsLevel actively pulls data from an external API, as will be demonstrated with PagerDuty. + +The process involves a two-stage approach: +1. **Extract**: Defines how to retrieve your data, including HTTP polling settings, authentication, and data extraction rules. +2. **Transform**: Defines how to map the extracted data to your OpsLevel catalog properties, create component types, and establish relationships between different objects. + +Both stages are configured in YAML, requiring no coding and allowing for configuration-driven integrations. + + +## Setup Instructions + +### Step 1: Customize the Service property schema + +**Define Custom Properties**: On the Component Edit Page, define a relevant custom property: + * **PD Service ID**: Type `Text` (String). + image + + +### Step 2: Create a Secret in OpsLevel for PagerDuty Authentication + +You'll need a secret to store your PagerDuty API token for authentication. + +1. **Navigate to Secrets**: In OpsLevel, go to **Settings > Secrets**. +2. **Create New Secret**: + * **Name**: `pd_token`. + +### Step 3: Create a Custom Integration Mapping in OpsLevel + +This step initiates the custom integration process. + +1. **Navigate to Integrations**: In OpsLevel, go to **Integrations**. +2. **Add Custom Integration**: Select the **Custom** integration option. +3. **Name the Integration**: You can name it `PagerDuty Service Sync`. + +### Step 4: Configure the Extract Definition + +The extract definition specifies how OpsLevel will pull data from PagerDuty. + +1. **Access Configuration**: Within your custom PagerDuty integration, find the **Extract and Transform Configuration** section. +2. **Define Extractor**: Configure the extractor definition in YAML as follows: + +```yaml +--- +extractors: +- external_kind: pagerduty_service + external_id: ".id" + http_polling: + url: https://api.pagerduty.com/services?limit=100 + method: GET + headers: + - name: Authorization + value: Token token={{ 'pd_token' | secret }} + - name: Accept + value: application/vnd.pagerduty+json;version=2 + iterator: ".services" +``` +* **`external_kind`**: A unique identifier for the type of data being extracted. +* **`external_id: ".id"`**: A JQ expression to select PagerDuty's Service ID as the unique identifier. +* **`http_polling`**: This section defines how OpsLevel will actively poll the PagerDuty API. +* **`method: GET`**: The HTTP method for the API call. +* **`iterator: ".services"`**: This JQ expression tells OpsLevel to iterate over each service in PagerDuty's response, treating each as an individual object. + +### Step 5: Configure the Transformation Definition + +The transformation definition maps the extracted PagerDuty data to your OpsLevel component properties. + +**Define Transformer**: Configure the transform definition in YAML as follows: + +```yaml +--- +transforms: +- external_kind: pagerduty_service + opslevel_kind: service + opslevel_identifier: .name | ascii_downcase | gsub(" "; "-") + on_component_not_found: skip + properties: + pd_service_id: ".id" + +``` +* **`external_kind: pagerduty_service`**: This maps the extracted PagerDuty data to the custom properties in OpsLevel. +* **`opslevel_kind: service`**: This maps PagerDuty services to the correct component type in OpsLevel (service). +* **`opslevel_identifier`**: Matches PagerDuty service names to OpsLevel service aliases (lowercased, spaces converted to dashes). If your PagerDuty service names don't align with your OpsLevel aliases, adjust this expression accordingly. +* **`on_component_not_found: skip`**: Silently skips any PagerDuty services that don't match an existing OpsLevel service alias, rather than creating new components or suggestions. Use `suggest` instead if you'd rather review unmatched services as detected component recommendations. + +### Step 6: Test and Sync the Integration + +After configuring both definitions, you can test and activate your integration. + +1. **Run Test**: Use the "Run Test" feature within the custom integration interface. This will execute the API call to PagerDuty, return the actual payload, and allow you to inspect the data, verifying how it maps to properties. +2. **Save Configuration**: Save your Extraction and Transform Definitions. +3. **Observe Components**: Once the sync completes, your services will have the `PD Service ID` property populated automatically. + * **Managed Properties**: Properties managed by the integration (e.g. `PD Service ID`) will be **locked** and cannot be updated directly from the OpsLevel UI or API; updates must come via the integration itself. + +### Step 7: Reference the property in a Custom Action + +Once `PD Service ID` is populated, you can reference it in a Custom Action's manual input using `defaultValueExpression`, avoiding the need to manually tag each service: + +```yaml +version: 1 +inputs: + - identifier: pd_service_id + displayName: PagerDuty Service ID + type: text_input + defaultValueExpression: .service.properties.pd_service_id + required: true +``` + +This is especially useful for Actions that trigger PagerDuty incidents directly from OpsLevel, where the Service ID needs to be passed in the payload. From e479f4104efa2bb8f0d412b4582bf76cc137f6aa Mon Sep 17 00:00:00 2001 From: synergy0225 Date: Thu, 23 Jul 2026 10:54:10 -0400 Subject: [PATCH 2/3] Add PagerDuty service ID custom integration mapping Adds a community example for syncing PagerDuty Service IDs into OpsLevel custom properties via a pull-based Custom Integration. Enables Custom Actions (e.g. triggering PagerDuty incidents) to reference the Service ID automatically without manual tagging. --- .../PagerDuty/README.md | 76 +++++++++++-------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/custom_integration_mapping/PagerDuty/README.md b/custom_integration_mapping/PagerDuty/README.md index 13934c6..1cd3421 100644 --- a/custom_integration_mapping/PagerDuty/README.md +++ b/custom_integration_mapping/PagerDuty/README.md @@ -19,9 +19,10 @@ Both stages are configured in YAML, requiring no coding and allowing for configu ### Step 1: Customize the Service property schema -**Define Custom Properties**: On the Component Edit Page, define a relevant custom property: +1. **Define Custom Properties**: On the Component Edit Page, define a relevant custom property: * **PD Service ID**: Type `Text` (String). - image + + image ### Step 2: Create a Secret in OpsLevel for PagerDuty Authentication @@ -47,49 +48,59 @@ The extract definition specifies how OpsLevel will pull data from PagerDuty. 1. **Access Configuration**: Within your custom PagerDuty integration, find the **Extract and Transform Configuration** section. 2. **Define Extractor**: Configure the extractor definition in YAML as follows: -```yaml ---- -extractors: -- external_kind: pagerduty_service - external_id: ".id" - http_polling: - url: https://api.pagerduty.com/services?limit=100 - method: GET - headers: - - name: Authorization - value: Token token={{ 'pd_token' | secret }} - - name: Accept - value: application/vnd.pagerduty+json;version=2 - iterator: ".services" -``` + ```yaml + --- + extractors: + - external_kind: pagerduty_service + external_id: ".id" + iterator: ".services" + http_polling: + method: GET + url: https://api.pagerduty.com/services?limit=100 + headers: + - name: Authorization + value: Token token={{ 'pd_token' | secret }} + - name: Accept + value: application/vnd.pagerduty+json;version=2 + ``` * **`external_kind`**: A unique identifier for the type of data being extracted. * **`external_id: ".id"`**: A JQ expression to select PagerDuty's Service ID as the unique identifier. +* **`iterator: ".services"`**: This JQ expression tells OpsLevel to iterate over each service in PagerDuty's response, treating each as an individual object. * **`http_polling`**: This section defines how OpsLevel will actively poll the PagerDuty API. * **`method: GET`**: The HTTP method for the API call. -* **`iterator: ".services"`**: This JQ expression tells OpsLevel to iterate over each service in PagerDuty's response, treating each as an individual object. +* **`limit=500`**: This config does not implement pagination handling. Setting `limit=500` (PagerDuty's documented maximum) avoids silent truncation. ### Step 5: Configure the Transformation Definition The transformation definition maps the extracted PagerDuty data to your OpsLevel component properties. -**Define Transformer**: Configure the transform definition in YAML as follows: - -```yaml ---- -transforms: -- external_kind: pagerduty_service - opslevel_kind: service - opslevel_identifier: .name | ascii_downcase | gsub(" "; "-") - on_component_not_found: skip - properties: - pd_service_id: ".id" - -``` +1. **Define Transformer**: Configure the transform definition in YAML as follows: + + ```yaml + --- + transforms: + - external_kind: pagerduty_service + opslevel_kind: service + opslevel_identifier: .name | ascii_downcase | gsub(" "; "-") + on_component_not_found: skip + properties: + pd_service_id: ".id" + ``` * **`external_kind: pagerduty_service`**: This maps the extracted PagerDuty data to the custom properties in OpsLevel. * **`opslevel_kind: service`**: This maps PagerDuty services to the correct component type in OpsLevel (service). -* **`opslevel_identifier`**: Matches PagerDuty service names to OpsLevel service aliases (lowercased, spaces converted to dashes). If your PagerDuty service names don't align with your OpsLevel aliases, adjust this expression accordingly. +* **`opslevel_identifier`**: This expression matches PagerDuty service names to OpsLevel service aliases by lowercasing the name and converting spaces to dashes. * **`on_component_not_found: skip`**: Silently skips any PagerDuty services that don't match an existing OpsLevel service alias, rather than creating new components or suggestions. Use `suggest` instead if you'd rather review unmatched services as detected component recommendations. +--- + +### Important Considerations + +* **Pagination**: This template does not paginate beyond `limit=500`. If you have more services than that in PagerDuty, some will not be synced. Verify your total service count in PagerDuty before relying on this integration for full coverage. +* **Alias matching**: The `opslevel_identifier` JQ expression above is a best-effort default, not a universal solution. Test it against a small number of services first (see Step 6) before trusting it across your full catalog. +* **JQ expressions**: Both the extractor and transform definitions use [JQ](https://jqlang.org/) syntax to parse and reshape API responses. If you're unfamiliar with JQ, [jqplay.org](https://jqplay.org) is a useful sandbox for testing expressions against sample PagerDuty payloads before wiring them into the live integration. + +--- + ### Step 6: Test and Sync the Integration After configuring both definitions, you can test and activate your integration. @@ -112,5 +123,6 @@ inputs: defaultValueExpression: .service.properties.pd_service_id required: true ``` +Note: `.service.properties.pd_service_id` resolves via the property's **identifier**, not its display name — confirm this matches the identifier shown on your property's Edit page (Step 1) if you named it something other than `pd_service_id`, since identifiers are auto-generated from the display name and may not match exactly (e.g. spaces, casing). This is especially useful for Actions that trigger PagerDuty incidents directly from OpsLevel, where the Service ID needs to be passed in the payload. From 3341994bbf2f025df57a58e71689872fd99a3e1a Mon Sep 17 00:00:00 2001 From: synergy0225 Date: Thu, 23 Jul 2026 16:03:54 -0400 Subject: [PATCH 3/3] Update README.md --- .../PagerDuty/README.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/custom_integration_mapping/PagerDuty/README.md b/custom_integration_mapping/PagerDuty/README.md index 1cd3421..0569e67 100644 --- a/custom_integration_mapping/PagerDuty/README.md +++ b/custom_integration_mapping/PagerDuty/README.md @@ -56,12 +56,12 @@ The extract definition specifies how OpsLevel will pull data from PagerDuty. iterator: ".services" http_polling: method: GET - url: https://api.pagerduty.com/services?limit=100 + url: https://api.pagerduty.com/services?limit=500 headers: - name: Authorization value: Token token={{ 'pd_token' | secret }} - name: Accept - value: application/vnd.pagerduty+json;version=2 + value: application/vnd.pagerduty+json;version=2 ``` * **`external_kind`**: A unique identifier for the type of data being extracted. * **`external_id: ".id"`**: A JQ expression to select PagerDuty's Service ID as the unique identifier. @@ -91,16 +91,6 @@ The transformation definition maps the extracted PagerDuty data to your OpsLevel * **`opslevel_identifier`**: This expression matches PagerDuty service names to OpsLevel service aliases by lowercasing the name and converting spaces to dashes. * **`on_component_not_found: skip`**: Silently skips any PagerDuty services that don't match an existing OpsLevel service alias, rather than creating new components or suggestions. Use `suggest` instead if you'd rather review unmatched services as detected component recommendations. ---- - -### Important Considerations - -* **Pagination**: This template does not paginate beyond `limit=500`. If you have more services than that in PagerDuty, some will not be synced. Verify your total service count in PagerDuty before relying on this integration for full coverage. -* **Alias matching**: The `opslevel_identifier` JQ expression above is a best-effort default, not a universal solution. Test it against a small number of services first (see Step 6) before trusting it across your full catalog. -* **JQ expressions**: Both the extractor and transform definitions use [JQ](https://jqlang.org/) syntax to parse and reshape API responses. If you're unfamiliar with JQ, [jqplay.org](https://jqplay.org) is a useful sandbox for testing expressions against sample PagerDuty payloads before wiring them into the live integration. - ---- - ### Step 6: Test and Sync the Integration After configuring both definitions, you can test and activate your integration. @@ -126,3 +116,13 @@ inputs: Note: `.service.properties.pd_service_id` resolves via the property's **identifier**, not its display name — confirm this matches the identifier shown on your property's Edit page (Step 1) if you named it something other than `pd_service_id`, since identifiers are auto-generated from the display name and may not match exactly (e.g. spaces, casing). This is especially useful for Actions that trigger PagerDuty incidents directly from OpsLevel, where the Service ID needs to be passed in the payload. + +--- + +### Important Considerations + +* **Pagination**: This template does not paginate beyond `limit=500`. If you have more services than that in PagerDuty, some will not be synced. Verify your total service count in PagerDuty before relying on this integration for full coverage. +* **Alias matching**: The `opslevel_identifier` JQ expression above is a best-effort default, not a universal solution. Test it against a small number of services first (see Step 6) before trusting it across your full catalog. +* **JQ expressions**: Both the extractor and transform definitions use [JQ](https://jqlang.org/) syntax to parse and reshape API responses. If you're unfamiliar with JQ, [jqplay.org](https://jqplay.org) is a useful sandbox for testing expressions against sample PagerDuty payloads before wiring them into the live integration. + +---