Merge branch 'master' into feature/EKSCatalog
Signed-off-by: Jonah Back <jonah@jonahback.com>
This commit is contained in:
@@ -3,12 +3,239 @@ id: org
|
||||
title: Microsoft Azure Active Directory Organizational Data
|
||||
sidebar_label: Org Data
|
||||
# prettier-ignore
|
||||
description: Importing users and groups from a Microsoft Azure Active Directory into Backstage
|
||||
description: Importing users and groups from Microsoft Azure Active Directory into Backstage
|
||||
---
|
||||
|
||||
The Backstage catalog can be set up to ingest organizational data - users and
|
||||
teams - directly from a tenant in Microsoft Azure Active Directory via the
|
||||
Microsoft Graph API.
|
||||
|
||||
More details on this are available in the
|
||||
[README of the `@backstage/plugin-catalog-backend-module-msgraph` package](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-msgraph/README.md).
|
||||
## 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 add --cwd packages/backend @backstage/plugin-catalog-backend-module-msgraph
|
||||
```
|
||||
|
||||
Next add the basic configuration to `app-config.yaml`
|
||||
|
||||
```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')
|
||||
```
|
||||
|
||||
Finally, register the plugin in `catalog.ts`.
|
||||
For large organizations, this plugin can take a long time, so be careful setting low frequency / timeouts.
|
||||
|
||||
```diff
|
||||
// packages/backend/src/plugins/catalog.ts
|
||||
+import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const builder = await CatalogBuilder.create(env);
|
||||
|
||||
+ builder.addEntityProvider(
|
||||
+ MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
|
||||
+ logger: env.logger,
|
||||
+ schedule: env.scheduler.createScheduledTaskRunner({
|
||||
+ frequency: { hours: 1 },
|
||||
+ timeout: { minutes: 50 },
|
||||
+ initialDelay: { seconds: 15}
|
||||
+ }),
|
||||
+ }),
|
||||
+ );
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
You will need to grant the same permissions to your identity as described for App Registrations above.
|
||||
|
||||
## Filtering imported Users and Groups
|
||||
|
||||
By default, the plugin will import all users and groups from your directory.
|
||||
This can be customized through filters and search queries.
|
||||
|
||||
### 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")'
|
||||
```
|
||||
|
||||
## 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`
|
||||
|
||||
```diff
|
||||
builder.addEntityProvider(
|
||||
MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
|
||||
// ...
|
||||
+ groupTransformer: myGroupTransformer,
|
||||
+ userTransformer: myUserTransformer,
|
||||
+ organizationTransformer: myOrganizationTransformer,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
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: 'aad',
|
||||
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 Azure Active Directory';
|
||||
}
|
||||
|
||||
return backstageUser;
|
||||
}
|
||||
|
||||
// Example organization transformer that removes the organization group completely
|
||||
export async function myOrganizationTransformer(
|
||||
graphOrganization: MicrosoftGraph.Organization,
|
||||
): Promise<GroupEntity | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### 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
|
||||
|
||||
@@ -74,7 +74,7 @@ The extension type is a simple one:
|
||||
|
||||
```ts
|
||||
export type Extension<T> = {
|
||||
expose(plugin: BackstagePlugin<any, any>): T;
|
||||
expose(plugin: BackstagePlugin): T;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user