...
Info |
---|
The info on this page applies to Grouper v4 and above. |
Links
Demo
- Movie: Grouper provisioning framework Azure demo (v2.6.15): https://youtu.be/abTkJVBMr1M
Config (grouper-loader.properties from version 2.6.15). Note, you should configure this in the provisioning configuration wizard.
Expand Code Block grouper.azureConnector.myAzure.clientId = 7e590d54-d6af-4b07-b2aXXXXXXX grouper.azureConnector.myAzure.clientSecret = ******* grouper.azureConnector.myAzure.graphEndpoint = https://graph.microsoft.com grouper.azureConnector.myAzure.graphVersion = beta grouper.azureConnector.myAzure.loginEndpoint = https://login.microsoftonline.com/ grouper.azureConnector.myAzure.resource = https://graph.microsoft.com grouper.azureConnector.myAzure.resourceEndpoint = https://graph.microsoft.com/beta/ grouper.azureConnector.myAzure.tenantId = 5e7fa4df-8d24-4c33-bdaXXXXXXXXXXX changeLog.consumer.provisioner_incremental_azureProvisioner.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer changeLog.consumer.provisioner_incremental_azureProvisioner.provisionerConfigId = azureProvisioner changeLog.consumer.provisioner_incremental_azureProvisioner.publisher.class = edu.internet2.middleware.grouper.app.provisioning.ProvisioningConsumer changeLog.consumer.provisioner_incremental_azureProvisioner.publisher.debug = false changeLog.consumer.provisioner_incremental_azureProvisioner.quartzCron = 0 * * * * ? otherJob.provisioner_full_azureProvisioner.class = edu.internet2.middleware.grouper.app.provisioning.GrouperProvisioningFullSyncJob otherJob.provisioner_full_azureProvisioner.provisionerConfigId = azureProvisioner otherJob.provisioner_full_azureProvisioner.quartzCron = 0 36 6 * * ? provisioner.azureProvisioner.addDisabledFullSyncDaemon = true provisioner.azureProvisioner.addDisabledIncrementalSyncDaemon = true provisioner.azureProvisioner.azureExternalSystemConfigId = myAzure provisioner.azureProvisioner.azureGroupType = true provisioner.azureProvisioner.class = edu.internet2.middleware.grouper.app.azure.GrouperAzureProvisioner provisioner.azureProvisioner.entityAttributeValueCache0entityAttribute = id provisioner.azureProvisioner.entityAttributeValueCache0has = true provisioner.azureProvisioner.entityAttributeValueCache0source = target provisioner.azureProvisioner.entityAttributeValueCache0type = entityAttribute provisioner.azureProvisioner.entityAttributeValueCacheHas = true provisioner.azureProvisioner.entityMatchingAttribute0name = userPrincipalName provisioner.azureProvisioner.entityMatchingAttributeCount = 1 provisioner.azureProvisioner.groupAttributeValueCache0groupAttribute = id provisioner.azureProvisioner.groupAttributeValueCache0has = true provisioner.azureProvisioner.groupAttributeValueCache0source = target provisioner.azureProvisioner.groupAttributeValueCache0type = groupAttribute provisioner.azureProvisioner.groupAttributeValueCacheHas = true provisioner.azureProvisioner.groupMatchingAttribute0name = displayName provisioner.azureProvisioner.groupMatchingAttributeCount = 1 provisioner.azureProvisioner.hasTargetEntityLink = true provisioner.azureProvisioner.hasTargetGroupLink = true provisioner.azureProvisioner.logAllObjectsVerbose = true provisioner.azureProvisioner.logCommandsAlways = true provisioner.azureProvisioner.numberOfEntityAttributes = 2 provisioner.azureProvisioner.numberOfGroupAttributes = 3 provisioner.azureProvisioner.operateOnGrouperEntities = true provisioner.azureProvisioner.operateOnGrouperGroups = true provisioner.azureProvisioner.operateOnGrouperMemberships = true provisioner.azureProvisioner.provisioningType = membershipObjects provisioner.azureProvisioner.selectAllEntities = true provisioner.azureProvisioner.showAdvanced = true provisioner.azureProvisioner.startWith = this is start with read only provisioner.azureProvisioner.subjectSourcesToProvision = jdbc provisioner.azureProvisioner.targetEntityAttribute.0.name = id provisioner.azureProvisioner.targetEntityAttribute.1.name = userPrincipalName provisioner.azureProvisioner.targetEntityAttribute.1.translateExpression = ${ grouperProvisioningEntity.subjectId + '@mchyzergmail.onmicrosoft.com' } provisioner.azureProvisioner.targetEntityAttribute.1.translateExpressionType = translationScript provisioner.azureProvisioner.targetGroupAttribute.0.insert = false provisioner.azureProvisioner.targetGroupAttribute.0.name = id provisioner.azureProvisioner.targetGroupAttribute.0.showAdvancedAttribute = true provisioner.azureProvisioner.targetGroupAttribute.0.showAttributeCrud = true provisioner.azureProvisioner.targetGroupAttribute.0.update = false provisioner.azureProvisioner.targetGroupAttribute.1.name = displayName provisioner.azureProvisioner.targetGroupAttribute.1.translateExpressionType = grouperProvisioningGroupField provisioner.azureProvisioner.targetGroupAttribute.1.translateFromGrouperProvisioningGroupField = extension provisioner.azureProvisioner.targetGroupAttribute.2.name = mailNickname provisioner.azureProvisioner.targetGroupAttribute.2.translateExpressionType = grouperProvisioningGroupField provisioner.azureProvisioner.targetGroupAttribute.2.translateFromGrouperProvisioningGroupField = extension
External system
grouper-loader.properties for local testing
Code Block |
---|
grouper.azureConnector.myAzure.clientId = 51e6dc4f-a85d-41c7-9569-8ac1b3159801 grouper.azureConnector.myAzure.clientSecret = ******* grouper.azureConnector.myAzure.graphEndpoint = https://graph.microsoft.com grouper.azureConnector.myAzure.graphVersion = beta grouper.azureConnector.myAzure.groupLookupAttribute = displayName grouper.azureConnector.myAzure.groupLookupValueFormat = ${group.getName()} grouper.azureConnector.myAzure.loginEndpoint = https://login.microsoftonline.com/ grouper.azureConnector.myAzure.resource = https://graph.microsoft.com grouper.azureConnector.myAzure.resourceEndpoint = https://graph.microsoft.com/beta/ grouper.azureConnector.myAzure.tenantId = 455754be-3a2b-40c9-acef-c425a92d7276 |
Provisioning fields and attributes
Item | Type | Description |
---|---|---|
id | field | uuid from Azure |
displayName | field | group name in Azure |
Azure group types
Grouper development team testing
Set this in grouper.hibernate.properties (or set env var: GROUPER_MOCK_SERVICES=true)
...
Code Block |
---|
grouper.azureConnector.azureTest.clientId = fd805xxxxdfb grouper.azureConnector.azureTest.clientSecret = ******* grouper.azureConnector.azureTest.graphEndpoint = https://graph.microsoft.com grouper.azureConnector.azureTest.graphVersion = v1.0 grouper.azureConnector.azureTest.loginEndpoint = http://localhost:8400/grouper/mockServices/azure/auth/ grouper.azureConnector.azureTest.resource = https://graph.microsoft.com grouper.azureConnector.azureTest.resourceEndpoint = http://localhost:8400/grouper/mockServices/azure/ grouper.azureConnector.azureTest.tenantId = 6c4dxxx0d |
Set up Azure
- Sign up with Azure
- On the left menu, go to Azure Active Directory
- Create a new app registration
- Select: Who can use this app: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
- After the app is registered, click on API Permissions and give Microsoft graph access
- Give full permissions for Directory, Group, User, and GroupMember
- Grant admin consent for default directory
- Check https://jwt.ms with the token, should see
- Permissions look like this. Note you can clamp down these permissions as needed
- From basic testing, if using read-only entities, the following Admin consent permissions seems to work (should not need any User consent grants):
- For even tight permissions, see below for setting the Grouper service account as the owner for new groups
- On the left, under Certificates and Secrets, create a new secret
- When testing using Postman, you will only need the secret value to get access token which will be used to call the graph API
- To get an access token, make a POST call to https://login.microsoftonline.com/a98c57b9-a771-4c01-b69b-83cceb36c834/oauth2/v2.0/token (id is the directory tenant id)
- Under form data send these four key values. client_id = clientId, scope = https://graph.microsoft.com/.default, client_secret = clientSecret, grant_type=client_credentials
- Content-type: application/x-www-form-urlencoded
Post body looks like this:
Code Block client_id=aea2eb2a-bc4f-4ae5-a315-3XXXXX&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&grant_type=client_credentials&client_secret=ewC8Q~yGN4dyBaSYBrOXXXXXXXXXX
Configure external system in grouper-loader.properties
Code Block grouper.azureConnector.azure.clientId = aea2eb2a-bc4f-4ae5-a315-38XXXXX grouper.azureConnector.azure.clientSecret = ewC8Q~yXXXXX grouper.azureConnector.azure.graphEndpoint = https://graph.microsoft.com grouper.azureConnector.azure.graphVersion = beta grouper.azureConnector.azure.loginEndpoint = https://login.microsoftonline.com/ grouper.azureConnector.azure.resource = https://graph.microsoft.com grouper.azureConnector.azure.resourceEndpoint = https://graph.microsoft.com/beta/ grouper.azureConnector.azure.tenantId = 5e7fa4df-8d24XXXXXXX
- The client id is the Application (client) ID next to Directory tenant id on the Overview page of the app.
- The response from the above POST call will give you an access token in the body which we will use to access graph APIs like https://graph.microsoft.com/v1.0/groups
- For the above request, send Authorization header with value Bearer <access token>
Add Microsoft certificate for graph apis
- Go to https://graph.microsoft.com/applications in your browser and download the certificate by clicking on the padlock sign in the address bar.
- Find out the path to the security directory inside the jre. e.g. /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security
From the terminal run "sudo keytool -import -alias microsoft.graph -keystore cacerts -file ~/graph.microsoft.com.cer
- For the password enter: changeit
Setting the Grouper service account as the owner of new groups (to reduce Azure privileges) v4.2.0+
If the service account Grouper uses for Graph API calls can be set as the owner of a managed group, the Azure application no longer needs the privileges to update all groups and memberships. It only needs the Group.Create privilege to create a new group, Group.Read.All to find groups to match with Grouper, and User.Read.All to resolve target entities. While there is an option in the Azure provisioner to set the groupOwner attribute with one or more entities, the ability to add non-users (i.e. service accounts) is only available v4.2.0.
...