Merge pull request #24222 from awanlin/topic/nbs-integration-azure-msgraph-docs

[NBS Docs] Updated the Azure and Microsoft Graph Integration Docs
This commit is contained in:
Patrik Oldsberg
2024-05-01 19:10:57 +02:00
committed by GitHub
4 changed files with 528 additions and 112 deletions
+187
View File
@@ -0,0 +1,187 @@
---
id: discovery--old
title: Azure DevOps Discovery
sidebar_label: Discovery
# prettier-ignore
description: Automatically discovering catalog entities from repositories in an Azure DevOps organization
---
:::info
This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./discovery.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Azure DevOps integration has a special entity provider for discovering
catalog entities within an Azure DevOps. The provider will crawl your Azure
DevOps organization and register entities matching the configured path. This can
be useful as an alternative to static locations or manually adding things to the
catalog.
This guide explains how to install and configure the Azure DevOps Entity Provider (recommended) or the Azure DevOps Processor.
## Dependencies
### Code Search Feature
Azure discovery is driven by the Code Search feature in Azure DevOps, this may not be enabled by default. For Azure
DevOps Services you can confirm this by looking at the installed extensions in your Organization Settings. For Azure
DevOps Server you'll find this information in your Collection Settings.
If the Code Search extension is not listed then you can install it from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms.vss-code-search&targetId=f9352dac-ba6e-434e-9241-a848a510ce3f&utm_source=vstsproduct&utm_medium=SearchExtStatus).
### Azure Integration
Setup [Azure integration](locations.md) with `host` and `token`. Host must be `dev.azure.com` for Cloud users, otherwise set this to your on-premise hostname.
## Installation
At your configuration, you add one or more provider configs:
```yaml title="app-config.yaml"
catalog:
providers:
azureDevOps:
yourProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: myproject
repository: service-* # this will match all repos starting with service-*
path: /catalog-info.yaml
schedule: # optional; same options as in TaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 3 }
yourSecondProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: '*' # this will match all projects
repository: '*' # this will match all repos
path: /catalog-info.yaml
anotherProviderId: # another identifier
organization: myorg
project: myproject
repository: '*' # this will match all repos
path: /src/*/catalog-info.yaml # this will search for files deep inside the /src folder
yetAnotherProviderId: # guess, what? Another one :)
host: selfhostedazure.yourcompany.com
organization: myorg
project: myproject
branch: development
```
The parameters available are:
- **`host:`** _(optional)_ Leave empty for Cloud hosted, otherwise set to your self-hosted instance host.
- **`organization:`** Your Organization slug (or Collection for on-premise users). Required.
- **`project:`** _(required)_ Your project slug. Wildcards are supported as shown on the examples above. Using '\*' will search all projects. For a project name containing spaces, use both single and double quotes as in `project: '"My Project Name"'`.
- **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched.
- **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml.
- **`branch:`** _(optional)_ The branch name to use.
- **`schedule`**:
- **`frequency`**:
How often you want the task to run. The system does its best to avoid overlapping invocations.
- **`timeout`**:
The maximum amount of time that a single task invocation can take.
- **`initialDelay`** _(optional)_:
The amount of time that should pass before the first invocation happens.
- **`scope`** _(optional)_:
`'global'` or `'local'`. Sets the scope of concurrency control.
_Note:_
- The path parameter follows the same rules as the search on Azure DevOps web interface. For more details visit the [official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops).
- To use branch parameters, it is necessary that the desired branch be added to the "Searchable branches" list within Azure DevOps Repositories. To do this, follow the instructions below:
1. Access your Azure DevOps and open the repository in which you want to add the branch.
2. Click on "Settings" in the lower-left corner of the screen.
3. Select the "Options" option in the left navigation bar.
4. In the "Searchable branches" section, click on the "Add" button to add a new branch.
5. In the window that appears, enter the name of the branch you want to add and click "Add".
6. The added branch will now appear in the "Searchable branches" list.
It may take some time before the branch is indexed and searchable.
As this provider is not one of the default providers, you will first need to install
the Azure catalog plugin:
```bash
# From your Backstage root directory
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-azure
```
Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`:
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure';
const builder = await CatalogBuilder.create(env);
/** ... other processors and/or providers ... */
/* highlight-add-start */
builder.addEntityProvider(
AzureDevOpsEntityProvider.fromConfig(env.config, {
logger: env.logger,
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
schedule: env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 30 },
timeout: { minutes: 3 },
}),
// optional: alternatively, use schedule
scheduler: env.scheduler,
}),
);
/* highlight-add-end */
```
## Alternative Processor
As an alternative to the entity provider `AzureDevOpsEntityProvider`, you can still use the `AzureDevopsDiscoveryProcessor`.
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
/* highlight-add-next-line */
builder.addProcessor(
AzureDevOpsDiscoveryProcessor.fromConfig(env.config, {
logger: env.logger,
}),
);
// ..
}
```
```yaml
catalog:
locations:
# Scan all repositories for a catalog-info.yaml in the root of the default branch
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject
# Or use a custom pattern for a subset of all repositories with default repository
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/service-*
# Or use a custom file format and location
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml
# And optionally provide a specific branch name using the version parameter
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/*?path=/catalog-info.yaml&version=GBtopic/catalog-info
```
Note the `azure-discovery` type, as this is not a regular `url` processor.
When using a custom pattern, the target is composed of these parts:
- The base instance URL, `https://dev.azure.com` in this case
- The organization name which is required, `myorg` in this case
- The project name which is optional, `myproject` in this case. This defaults to \*, which scans all the projects where the token has access to.
- The repository blob to scan, which accepts \* wildcard tokens and must be
added after `_git/`. This can simply be `*` to scan all repositories in the
project.
- The path within each repository to find the catalog YAML file. This will
usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar
variation for catalog files stored in the root directory of each repository.
- The repository branch to scan which is optional, `topic/catalog-info` in this case. If omitted, the repo's default branch will be scanned. The `GB` prefix is required, as this is how Azure DevOps identifies the version as a branch.
+9 -75
View File
@@ -6,6 +6,10 @@ sidebar_label: Discovery
description: Automatically discovering catalog entities from repositories in an Azure DevOps organization
---
:::info
This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./discovery--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Azure DevOps integration has a special entity provider for discovering
catalog entities within an Azure DevOps. The provider will crawl your Azure
DevOps organization and register entities matching the configured path. This can
@@ -30,7 +34,7 @@ Setup [Azure integration](locations.md) with `host` and `token`. Host must be `d
## Installation
At your configuration, you add one or more provider configs:
In your configuration, you add one or more provider configs:
```yaml title="app-config.yaml"
catalog:
@@ -103,81 +107,11 @@ the Azure catalog plugin:
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-azure
```
Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`:
Then updated your backend by adding the following line:
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure';
const builder = await CatalogBuilder.create(env);
/** ... other processors and/or providers ... */
```ts title="packages/backend/src/index.ts"
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
/* highlight-add-start */
builder.addEntityProvider(
AzureDevOpsEntityProvider.fromConfig(env.config, {
logger: env.logger,
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
schedule: env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 30 },
timeout: { minutes: 3 },
}),
// optional: alternatively, use schedule
scheduler: env.scheduler,
}),
);
backend.add(import('@backstage/plugin-catalog-backend-module-azure/alpha'));
/* highlight-add-end */
```
## Alternative Processor
As an alternative to the entity provider `AzureDevOpsEntityProvider`, you can still use the `AzureDevopsDiscoveryProcessor`.
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
/* highlight-add-next-line */
builder.addProcessor(
AzureDevOpsDiscoveryProcessor.fromConfig(env.config, {
logger: env.logger,
}),
);
// ..
}
```
```yaml
catalog:
locations:
# Scan all repositories for a catalog-info.yaml in the root of the default branch
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject
# Or use a custom pattern for a subset of all repositories with default repository
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/service-*
# Or use a custom file format and location
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml
# And optionally provide a specific branch name using the version parameter
- type: azure-discovery
target: https://dev.azure.com/myorg/myproject/_git/*?path=/catalog-info.yaml&version=GBtopic/catalog-info
```
Note the `azure-discovery` type, as this is not a regular `url` processor.
When using a custom pattern, the target is composed of these parts:
- The base instance URL, `https://dev.azure.com` in this case
- The organization name which is required, `myorg` in this case
- The project name which is optional, `myproject` in this case. This defaults to \*, which scans all the projects where the token has access to.
- The repository blob to scan, which accepts \* wildcard tokens and must be
added after `_git/`. This can simply be `*` to scan all repositories in the
project.
- The path within each repository to find the catalog YAML file. This will
usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar
variation for catalog files stored in the root directory of each repository.
- The repository branch to scan which is optional, `topic/catalog-info` in this case. If omitted, the repo's default branch will be scanned. The `GB` prefix is required, as this is how Azure DevOps identifies the version as a branch.
+274
View File
@@ -0,0 +1,274 @@
---
id: org--old
title: Microsoft Entra Tenant Data
sidebar_label: Org Data
# prettier-ignore
description: Importing users and groups from Microsoft Entra ID into Backstage
---
:::info
This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./org.md) instead.Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Backstage catalog can be set up to ingest organizational data - users and
teams - directly from a tenant in Microsoft Entra ID via the
Microsoft Graph API.
## Installation
The package is not installed by default, therefore you have to add `@backstage/plugin-catalog-backend-module-msgraph` to your backend package.
```bash
# From your Backstage root directory
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-msgraph
```
Next add the basic configuration to `app-config.yaml`
```yaml title="app-config.yaml"
catalog:
providers:
microsoftGraphOrg:
default:
tenantId: ${AZURE_TENANT_ID}
user:
filter: accountEnabled eq true and userType eq 'member'
group:
filter: >
securityEnabled eq false
and mailEnabled eq true
and groupTypes/any(c:c+eq+'Unified')
schedule:
frequency: PT1H
timeout: PT50M
```
Finally, register the plugin in `catalog.ts`.
For large organizations, this plugin can take a long time, so be careful setting low frequency / timeouts and importing a large amount of users / groups for the first try.
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
/* highlight-add-start */
builder.addEntityProvider(
MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
logger: env.logger,
scheduler: env.scheduler,
}),
);
/* highlight-add-end */
// ..
}
```
## Authenticating with Microsoft Graph
### Local Development
For a local dev environment, it's recommended you have the Azure CLI or Azure PowerShell installed, and are logged in to those.
Alternatively you can use VSCode with the Azure extension if you install `@azure/identity-vscode`.
When these are set up, the plugin will authenticate with the Microsoft Graph API without you needing to configure any credentials, or granting any special permissions.
If you can't do this, you'll have to create an App Registration.
### App Registration
If none of the other authentication methods work, you can create an app registration in the azure portal.
By default the graph plugin requires the following Application permissions (not Delegated) for Microsoft Graph:
- `GroupMember.Read.All`
- `User.Read.All`
If your organization required Admin Consent for these permissions, that will need to be granted.
When authenticating with a ClientId/ClientSecret, you can either set the `AZURE_TENANT_ID`, `AZURE_CLIENT_ID` and `AZURE_CLIENT_SECRET` environment variables, or specify the values in configuration
```yaml
microsoftGraphOrg:
default:
##...
clientId: 9ef1aac6-b454-4e69-9cf5-7199df049281
clientSecret: REDACTED
```
To authenticate with a certificate rather than a client secret, you can set the `AZURE_TENANT_ID`, `AZURE_CLIENT_ID` and `AZURE_CLIENT_CERTIFICATE_PATH` environments
### Managed Identity
If deploying to resources that supports Managed Identity, and has identities configured (e.g. Azure App Services, Azure Container Apps), Managed Identity should be picked up without any additional configuration.
If your app has multiple managed identities, you may need to set the `AZURE_CLIENT_ID` environment variable to tell Azure Identity which identity to use.
To grant the managed identity the same permissions as mentioned in _App Registration_ above, [please follow this guide](https://docs.microsoft.com/en-us/azure/app-service/tutorial-connect-app-access-microsoft-graph-as-app-javascript?tabs=azure-powershell)
## Filtering imported Users and Groups
By default, the plugin will import all users and groups from your directory.
This can be customized through [filters](https://learn.microsoft.com/en-us/graph/filter-query-parameter) and [search](https://learn.microsoft.com/en-us/graph/search-query-parameter) queries. Keep in mind that if you omit filters and search queries for the user or group properties, the plugin will automatically import all available users or groups.
### Groups
A smaller set of groups can be obtained by configuring a search query or a filter.
If both `filter` and `search` are provided, then groups must match both to be ingested.
```yaml
microsoftGraphOrg:
providerId:
group:
filter: securityEnabled eq false and mailEnabled eq true and groupTypes/any(c:c+eq+'Unified')
search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
```
In addition to these groups, one additional group will be created for your organization.
All imported groups will be a child of this group.
### Users
There are two modes for importing users - You can import all user objects matching a `filter`.
```yaml
microsoftGraphOrg:
providerId:
user:
filter: accountEnabled eq true and userType eq 'member'
```
Alternatively you can import users that are members of specific groups.
For each group matching the `search` and `filter` query, each group member will be imported.
Only direct group members will be imported, not transient users.
```yaml
microsoftGraphOrg:
providerId:
userGroupMember:
filter: "displayName eq 'Backstage Users'"
search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
```
### User photos
By default, the photos of users will be fetched and added to each user entity. For huge organizations this may be unfeasible, as it will take a _very_ long time, and can be disabled by setting `loadPhotos` to `false`:
```yaml
microsoftGraphOrg:
providerId:
user:
filter: ...
loadPhotos: false
```
## Customizing Transformation
Ingested entities can be customized by providing custom transformers.
These can be used to completely replace the built in logic, or used to tweak it by using the default transformers (`defaultGroupTransformer`, `defaultUserTransformer` and `defaultOrganizationTransformer`
Entities can also be excluded from backstage by returning `undefined`.
These Transformers are be registered when configuring `MicrosoftGraphOrgEntityProvider`
```ts
builder.addEntityProvider(
MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
// ...
/* highlight-add-start */
groupTransformer: myGroupTransformer,
userTransformer: myUserTransformer,
organizationTransformer: myOrganizationTransformer,
/* highlight-add-end */
}),
);
```
When using custom transformers, you may want to customize the data returned.
Several configuration options can be provided to tweak the Microsoft Graph query to get the data you need
```yaml
microsoftGraphOrg:
providerId:
user:
expand: manager
group:
expand: member
select: ['id', 'displayName', 'description']
```
The following provides an example of each kind of transformer
```ts
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
import {
defaultGroupTransformer,
defaultUserTransformer,
defaultOrganizationTransformer,
} from '@backstage/plugin-catalog-backend-module-msgraph';
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
// This group transformer completely replaces the built in logic with custom logic.
export async function myGroupTransformer(
group: MicrosoftGraph.Group,
groupPhoto?: string,
): Promise<GroupEntity | undefined> {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: group.id!,
annotations: {},
},
spec: {
type: 'Microsoft Entra ID',
children: [],
},
};
}
// This user transformer makes use of the built in logic, but also sets the description field
export async function myUserTransformer(
graphUser: MicrosoftGraph.User,
userPhoto?: string,
): Promise<UserEntity | undefined> {
const backstageUser = await defaultUserTransformer(graphUser, userPhoto);
if (backstageUser) {
backstageUser.metadata.description = 'Loaded from Microsoft Entra ID';
}
return backstageUser;
}
// Example organization transformer that removes the organization group completely
export async function myOrganizationTransformer(
graphOrganization: MicrosoftGraph.Organization,
): Promise<GroupEntity | undefined> {
return undefined;
}
```
## Troubleshooting
### No data
First check your logs for the message `Reading msgraph users and groups`.
If you don't see this, check you've registered the provider, and that the schedule is valid
If you see a log entry `Read 0 msgraph users and 0 msgraph groups`, check your search and filter arguments.
If you see the start message (`Reading msgraph users and groups`) but no end message (`Read X msgraph users and Y msgraph groups`), then it is likely the job is taking a long time due to a large volume of data.
The default behavior is to import all users and groups, which is often more data than needed.
Try importing a smaller set of data (e.g. `filter: displayName eq 'John Smith'`).
### Authentication / Token Errors
See [Troubleshooting Azure Identity Authentication Issues](https://aka.ms/azsdk/js/identity/troubleshoot)
### Error while reading users from Microsoft Graph: Authorization_RequestDenied - Insufficient privileges to complete the operation
- Make sure you've granted all the required permissions to your application registration or managed identity
- Make sure the permissions are `Application` permissions rather than `Delegated`
- If your organization has configured "Admin consent" to be required, make sure this has been granted for your application permissions
- If your group queries are returning Microsoft Teams groups, you may need to grant addition permissions (e.g. `Team.ReadBasic.All`, `TeamMember.Read.All`)
- If you've added additional `select` or `expand` fields, those may need additional permissions granted
+58 -37
View File
@@ -6,6 +6,10 @@ sidebar_label: Org Data
description: Importing users and groups from Microsoft Entra ID into Backstage
---
:::info
This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./org--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Backstage catalog can be set up to ingest organizational data - users and
teams - directly from a tenant in Microsoft Entra ID via the
Microsoft Graph API.
@@ -39,29 +43,17 @@ catalog:
timeout: PT50M
```
Finally, register the plugin in `catalog.ts`.
:::note
For large organizations, this plugin can take a long time, so be careful setting low frequency / timeouts and importing a large amount of users / groups for the first try.
:::
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph';
Finally, updated your backend by adding the following line:
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
/* highlight-add-start */
builder.addEntityProvider(
MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
logger: env.logger,
scheduler: env.scheduler,
}),
);
/* highlight-add-end */
// ..
}
```ts title="packages/backend/src/index.ts"
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
/* highlight-add-start */
backend.add(import('@backstage/plugin-catalog-backend-module-msgraph/alpha'));
/* highlight-add-end */
```
## Authenticating with Microsoft Graph
@@ -164,21 +156,6 @@ Ingested entities can be customized by providing custom transformers.
These can be used to completely replace the built in logic, or used to tweak it by using the default transformers (`defaultGroupTransformer`, `defaultUserTransformer` and `defaultOrganizationTransformer`
Entities can also be excluded from backstage by returning `undefined`.
These Transformers are be registered when configuring `MicrosoftGraphOrgEntityProvider`
```ts
builder.addEntityProvider(
MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
// ...
/* highlight-add-start */
groupTransformer: myGroupTransformer,
userTransformer: myUserTransformer,
organizationTransformer: myOrganizationTransformer,
/* highlight-add-end */
}),
);
```
When using custom transformers, you may want to customize the data returned.
Several configuration options can be provided to tweak the Microsoft Graph query to get the data you need
@@ -192,9 +169,53 @@ microsoftGraphOrg:
select: ['id', 'displayName', 'description']
```
The following provides an example of each kind of transformer
### Using Custom Transformers
```ts
Transformers can be configured by extending `microsoftGraphOrgEntityProviderTransformExtensionPoint`. Here is an example:
```ts title="packages/backend/src/index.ts"
import { createBackendModule } from '@backstage/backend-plugin-api';
import { microsoftGraphOrgEntityProviderTransformExtensionPoint } from '@backstage/plugin-catalog-backend-module-msgraph/alpha';
import {
myUserTransformer,
myGroupTransformer,
myOrganizationTransformer,
} from './transformers';
backend.add(
createBackendModule({
pluginId: 'catalog',
moduleId: 'microsoft-graph-extensions',
register(env) {
env.registerInit({
deps: {
/* highlight-add-start */
microsoftGraphTransformers:
microsoftGraphOrgEntityProviderTransformExtensionPoint,
/* highlight-add-end */
},
async init({ microsoftGraphTransformers }) {
/* highlight-add-start */
microsoftGraphTransformers.setUserTransformer(myUserTransformer);
microsoftGraphTransformers.setGroupTransformer(myGroupTransformer);
microsoftGraphTransformers.setOrganizationTransformer(
myOrganizationTransformer,
);
/* highlight-add-end */
},
});
},
}),
);
```
The `myUserTransformer`, `myGroupTransformer`, and `myOrganizationTransformer` transformer functions are from the examples in the section below.
### Transformer Examples
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.
```ts title="packages/backend/src/transformers.ts"
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
import {
defaultGroupTransformer,