From 95491d2fa4608708027ea343f50bef9b687ba942 Mon Sep 17 00:00:00 2001 From: darylgraham Date: Tue, 5 Nov 2024 23:16:02 +0000 Subject: [PATCH] Extend Azure Org custom transformer docs to be more end-to-end Signed-off-by: darylgraham --- docs/integrations/azure/org.md | 139 +++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 7 deletions(-) diff --git a/docs/integrations/azure/org.md b/docs/integrations/azure/org.md index e6fb69a520..6e8b9c84a3 100644 --- a/docs/integrations/azure/org.md +++ b/docs/integrations/azure/org.md @@ -258,21 +258,96 @@ The `myUserTransformer`, `myGroupTransformer`, `myOrganizationTransformer`, and The following provides an example of each kind of transformer. We recommend creating a `transformers.ts` file in your `packages/backend/src` folder for these. +First, lets set up the basic structure of the file, with functions for each kind of transformer that simply passes through the default transformer unchanged. + ```ts title="packages/backend/src/transformers.ts" import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import { defaultGroupTransformer, defaultUserTransformer, defaultOrganizationTransformer, + microsoftGraphOrgEntityProviderTransformExtensionPoint, MicrosoftGraphProviderConfig, } from '@backstage/plugin-catalog-backend-module-msgraph'; import { GroupEntity, UserEntity } from '@backstage/catalog-model'; +import { createBackendModule } from '@backstage/backend-plugin-api'; -// This group transformer completely replaces the built in logic with custom logic. +// The Group transformer transforms Groups that are ingested from MS Graph export async function myGroupTransformer( group: MicrosoftGraph.Group, groupPhoto?: string, ): Promise { + const backstageGroup = await defaultGroupTransformer(group, groupPhoto); + return backstageGroup; +} + +// The User transformer transforms Users that are ingested from MS Graph +export async function myUserTransformer( + graphUser: MicrosoftGraph.User, + userPhoto?: string, +): Promise { + const backstageUser = await defaultUserTransformer(graphUser, userPhoto); + return backstageUser; +} + +// The Organization transformer transforms the root MS Graph Organization into a Group +export async function myOrganizationTransformer( + graphOrganization: MicrosoftGraph.Organization, +): Promise { + const backstageOrg = await defaultOrganizationTransformer(graphOrganization); + return backstageOrg; +} + +// The Provider Config transformer enables modification of the plugin config +export async function myProviderConfigTransformer( + provider: MicrosoftGraphProviderConfig, +): Promise { + return provider; +} + +// Wrapping these functions in a Module allows us to inject them into the Catalog plugin easily +export const myMsgraphTransformersModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'msgraph-org', + register(reg) { + reg.registerInit({ + deps: { + microsoftGraphTransformers: + microsoftGraphOrgEntityProviderTransformExtensionPoint, + }, + async init({ microsoftGraphTransformers }) { + // Set the transformers to our custom functions + microsoftGraphTransformers.setUserTransformer(myUserTransformer); + microsoftGraphTransformers.setGroupTransformer(myGroupTransformer); + microsoftGraphTransformers.setOrganizationTransformer( + myOrganizationTransformer, + ); + microsoftGraphTransformers.setProviderConfigTransformer( + myProviderConfigTransformer, + ); + }, + }); + }, +}); + +// Export a default to make importing into the backend simpler +export default myMsgraphTransformersModule; +``` + +Now lets customize each of the providers to suit our needs. + +The Group Transformer will have the default logic completely removed and replaced with our custom logic: + +```ts +export async function myGroupTransformer( + group: MicrosoftGraph.Group, + groupPhoto?: string, +): Promise { + // highlight-remove-start + const backstageGroup = await defaultGroupTransformer(group, groupPhoto); + return backstageGroup; + // highlight-remove-end + // highlight-add-start return { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', @@ -285,40 +360,90 @@ export async function myGroupTransformer( children: [], }, }; + // highlight-add-end } +``` -// This user transformer makes use of the built in logic, but also sets the description field +The User Transformer makes use of the built-in logic, but also modifies the username and sets a description + +```ts export async function myUserTransformer( graphUser: MicrosoftGraph.User, userPhoto?: string, ): Promise { const backstageUser = await defaultUserTransformer(graphUser, userPhoto); - + // highlight-add-start + // Make sure the default transformer returned an entity if (backstageUser) { - backstageUser.metadata.description = 'Loaded from Microsoft Entra ID'; + // Update the description to make it obvious where this entity came from + backstageUser.metadata.description = + 'Loaded from Microsoft Entra ID via MyCustomUserTransformer'; + + // The default transformer sets the username to the email address with invalid characters subbed out: 'user_domain.com' + // Set the username to the local part of the email address in lowercase without the domain + const newName = backstageUser.metadata.name.split('_')[0].toLowerCase(); + backstageUser.metadata.name = newName; + + return backstageUser; } - + return undefined; + // highlight-add-end + // highlight-remove-start return backstageUser; + // highlight-remove-end } +``` -// Example organization transformer that removes the organization group completely +The Organization Transformer removes the organization group completely by returning undefined + +```ts export async function myOrganizationTransformer( graphOrganization: MicrosoftGraph.Organization, ): Promise { + // highlight-remove-start + const backstageOrg = await defaultOrganizationTransformer(graphOrganization); + return backstageOrg; + // highlight-remove-end + // highlight-add-start return undefined; + // highlight-add-end } +``` -// Example config transformer that expands the group filter to also include 'azure-group-a' +The Config Transformer expands the group filter to also include 'azure-group-a' + +```ts export async function myProviderConfigTransformer( provider: MicrosoftGraphProviderConfig, ): Promise { + // highlight-add-start if (!provider.groupFilter?.includes('azure-group-a')) { provider.groupFilter = `${provider.groupFilter} or displayName eq 'azure-group-a'`; } + // highlight-add-end return provider; } ``` +Now we just need to add our new module to the Backend. + +```ts +// packages/backend/src/index.ts +// Your file will have more than this in it + +const backend = createBackend(); + +... + +// highlight-add-start +backend.add(import('./transformers')); +// highlight-add-end + +... + +backend.start(); +``` + ## Troubleshooting ### No data