entra-id
entra-id-governance
]
automating full synchronization cycle start of an HR driven provisioning connector app
Recently I had a task to start full synchronization cycle of HR driven provisioning enterprise application programmatically.
According to Microsoft documentation you just need to run following:
Import-Module Microsoft.Graph.Applications
$params = @{
criteria = @{
resetScope = "Full"
}
}
# we want to restart the synchronization job
Restart-MgServicePrincipalSynchronizationJob -ServicePrincipalId $servicePrincipalId -SynchronizationJobId $synchronizationJobId -BodyParameter $params
Even though it seems quite simple, in this post I’ll elaborate on:
- parameters gathering, e.g.:
- how do I get the
$servicePrincipalId
and$synchronizationJobId
parameters?
- how do I get the
- authorization part, how should I:
- create a service principal under which context will the full sync scheduler / invocation script run
- assign minimal rights to the app registration/service principal for automating the run
- put the rights/permissions/grants to “configured permissions list” via MgGraph
- scripts, I’ll provide:
- full script to set-up all necessary prerequisites
- a script to run the full synchronization
Parameters gathering
You can get the $servicePrincipalId
and $synchronizationJobId
via different means:
- Entra ID portal
- MgGraph PowerShell
Gathering parameters via Entra ID portal
To get the $servicePrincipalId
via Entra ID portal use following steps.
- Open the enterprise application blade in Entra ID portal
- Find the respective HR driven provisioning connector application, choose Properties
- Choose/Copy Object ID, this is the
$servicePrincipalId
To get the $synchronizationJobId
via Entra ID portal use following steps.
- Open the enterprise application blade in Entra ID portal
- Find the respective HR driven provisioning connector application, choose Provisioning
- Select the View technical information and copy Job ID
Gathering parameters via MgGraph
Run following script, you can filter the application by displayName, in this case it is SuccessFactors to Active Directory User Provisioning.
$servicePrincipalId = Get-MgServicePrincipal -Filter "displayName eq 'SuccessFactors to Active Directory User Provisioning'" | select -ExpandProperty Id
$synchronizationJobId = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId $servicePrincipalId | Select -ExpandProperty Id
So now we have the necessary parameters to run the synchronization scheduler script. We still need to assign correct rights.
Authorization part
FullSyncScheduler service principal creation
In an ideal world I would use an azure function and managed identity.
Unfortunately I needed to run the scheduling script from on-premises environment, so I needed to create an app registration.
I’ve also uploaded a certificate which can be used to authenticate as principal.
Granting rights to FullSyncScheduler
Since we are running under service principal context - FullSyncScheduler service principal, we need application level permissions.
According to documentation you need following rights.
Permission type | Least privileged permissions | Higher privileged permissions |
---|---|---|
Application | Application.ReadWrite.OwnedBy | Synchronization.ReadWrite.All |
Synchronization.ReadWrite.All is pretty powerful as it allows the application to configure the Azure AD synchronization service.
This basically means that the service principal with such rights could configure any provisioning application within your Entra ID tenant (including SCIM provisioning, excluding on-premises hybrid synchronizations such Entra Connect Sync or Entra Cloud Sync).
If we don’t want to grant such powerful permissions, we need to make the FullSyncScheduler service principal make an application owner. Please note that this cannot be done via GUI/browser and you’ll need Graph for the assignment.
# Connect to MgGraph, we'll need to write to the application
Connect-MgGraph -Scopes Application.ReadWrite.All
# get the FullSyncScheduler service principal Id
$fullsyncServicePrincipalId = Get-MgServicePrincipal -Filter "displayName eq 'FullSyncScheduler'" | select -ExpandProperty Id
# assign owner of the SuccessFactors to "Active Directory User Provisioning" application
$params = @{
"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$fullsyncServicePrincipalId"
}
New-MgServicePrincipalOwnerByRef -ServicePrincipalId $servicePrincipalId -BodyParameter $params
Finally we will need to grant Application.ReadWrite.OwnedBy
application admin consent. We do this again by PowerShell
Connect-MgGraph -Scopes "Application.ReadWrite.All AppRoleAssignment.ReadWrite.All"
# get Graph Resource
$graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'"
# find role
$appRoleRWOwnedBy=$GraphServicePrincipal.AppRoles | ?{$_.Value -eq 'Application.ReadWrite.OwnedBy'}
$params = @{
principalId = "$fullsyncServicePrincipalId"
resourceId = "$($graphServicePrincipal.Id)"
appRoleId = "$($appRoleRWOwnedBy.Id)"
}
# grant the consent
New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $fullsyncServicePrincipalId -BodyParameter $params
You can check that permissions were granted successfully in App registrations. You’ll see that the permissions are granted, however they are not part of configured permissions list.
I could not find any way how to put the permissions on “configured permissions
list”, so I used Merill’s Graph X-Ray
extension to sniff out how it is being done in
Entra ID portal. Unfortunately no direct command was fetched by Graph X-Ray,
though you can clearly see that a PATCH
verb was used against an
application registration.
I checked the network trace to reverse engineer the necessary call. The result is included in following code snippet.
BEWARE
The code snippet does not check if there
are any other permissions assigned to the FullSyncScheduler. So if you have
other permissions assigned in “configured permissions list” it will overwrite
those. To accommodate existing permissions, you need to combine the existing
requiredResourceAccess
with the newly added one. This exercise is out-of-scope and is left to the reader as homework .
# Connect to MgGraph, we'll need to write to the application
Connect-MgGraph -Scopes Application.ReadWrite.All
$fullSyncEntrepriseApp=Get-MgApplication -Filter "displayName eq 'FullSyncScheduler'"
$graphAppId=$graphServicePrincipal.AppId
$params = @{
requiredResourceAccess = @(
@{
resourceAppId = "$graphAppId"
resourceAccess = @(
@{
id = "$($appRoleRWOwnedBy.Id)"
type = "Role"
}
)
}
)
}
# Update the application
Update-MgApplication -ApplicationId $$fullSyncEntrepriseApp.Id -BodyParameter $params
Scripts
The code snippets in this short post can be summarized into:
- setup script,
- full synchronization script.
Setup script
<# PARAMETERS #>
$schedulerAppName="FullSyncScheduler"
$successFactorsProvisioningAppName="SuccessFactors to Active Directory User Provisioning"
$authCertificate=ls Cert:\CurrentUser\my\4312ad9dfb84c891aa3ef5bd137b083449e63931
<# END OF PARAMETERS #>
try
{
Connect-MgGraph -Scopes "Application.ReadWrite.All AppRoleAssignment.ReadWrite.All"
# Create Application Registration
Write-Host "Creating application registration: $schedulerAppName ..."
$schedulerAppRegistration=New-MgApplication -DisplayName $schedulerAppName
Write-Host " OK!"
# Upload Certificate to Application Registration
Write-Host "Creating uploading certificate for certificate authentication to: $schedulerAppName ..."
$params = @{
keyCredentials = @(
@{
endDateTime = $authCertificate.NotAfter
startDateTime = $authCertificate.NotBefore
type = "AsymmetricX509Cert"
usage = "Verify"
key = [System.Text.Encoding]::ASCII.GetBytes("$([System.Convert]::ToBase64String($authCertificate.RawData))")
displayName = $authCertificate.GetNameInfo("Simple",$false)
}
)
}
$mgResult=Update-MgApplication -ApplicationId $schedulerAppRegistration.Id -BodyParameter $params
Write-Host " OK!"
# assign owner rights of the application SuccessFactorsProvisioningServicePrincipal to the FullSyncSchedulerServicePrincipal
Write-Host "Assigning owner rights on SuccessFactorsProvisioningServicePrincipal to FullSyncSchedulerServicePrincipal..."
# Creating schedulerAppServicePrincipal
$schedulerAppServicePrincipal= New-MgServicePrincipal -AppId $schedulerAppRegistration.AppId
# Searching for SuccessFactorsProvisioning Service Principal
$successFactorsProvisioningServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq '$successFactorsProvisioningAppName'"
$params = @{
"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($schedulerAppServicePrincipal.Id)"
}
$mgResult=New-MgServicePrincipalOwnerByRef -ServicePrincipalId $successFactorsProvisioningServicePrincipal.Id -BodyParameter $params
Write-Host " OK!"
# Grant the consent for Application.ReadWrite.OwnedBy
Write-Host "Granting admin consent for MSGraph/Application.ReadWrite.OwnedBy to FullSyncSchedulerServicePrincipal..."
# get Graph Resource
$graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'"
# find role
$appRoleRWOwnedBy=$GraphServicePrincipal.AppRoles | ?{$_.Value -eq 'Application.ReadWrite.OwnedBy'}
$params = @{
principalId = "$($schedulerAppServicePrincipal.Id)"
resourceId = "$($graphServicePrincipal.Id)"
appRoleId = "$($appRoleRWOwnedBy.Id)"
}
$mgResult=New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $($schedulerAppServicePrincipal.Id) -BodyParameter $params
Write-Host " OK!"
# Add permissions to configured permissions list
Write-Host "Adding admin consent/OAuth scopes FullSyncScheduler application to configured permissions list..."
$params = @{
requiredResourceAccess = @(
@{
resourceAppId = "$($graphServicePrincipal.AppId)"
resourceAccess = @(
@{
id = "$($appRoleRWOwnedBy.Id)"
type = "Role"
}
)
}
)
}
# Update the application
$mgResult=Update-MgApplication -ApplicationId $schedulerAppRegistration.Id -BodyParameter $params
Write-Host " OK!"
}catch
{
throw $_
}
Full synchronization script
<# PARAMETERS #>
# FullSyncScheduler clientId, e.g.: Get-MgApplication -filter "displayName eq 'FullSyncScheduler'" | select -ExpandProperty AppId
$clientId='e6f278e4-8527-435b-8164-cf09359e9a6d'
# authentication certificate uploaded during setup
$authCertificate=ls Cert:\CurrentUser\my\4312ad9dfb84c891aa3ef5bd137b083449e63931
# tenantId, pretty self-explanatory
$tenantId='46d764b9-0122-4319-8731-df37b7b4fcb8' # put your tenant Id here
$successFactorsProvisioningAppName="SuccessFactors to Active Directory User Provisioning"
<# END OF PARAMETERS #>
try
{
$params = @{
criteria = @{
resetScope = "Full"
}
}
# we are automating in service context
Connect-MgGraph -ClientId $clientId -Certificate $authCertificate -TenantId $tenantId
$servicePrincipalId = Get-MgServicePrincipal -Filter "displayName eq '$successFactorsProvisioningAppName'" | select -ExpandProperty Id
$synchronizationJobId = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId $servicePrincipalId | Select -ExpandProperty Id
# we want to restart the synchronization job
Restart-MgServicePrincipalSynchronizationJob -ServicePrincipalId $servicePrincipalId -SynchronizationJobId $synchronizationJobId -BodyParameter $params
}catch
{
throw $_
}
That’s it for today have fun and snacks .