AutoMate Technisch Help

IAM HR Sync - Pipeline Architectuur

Overzicht

De IAM HR Sync pipeline gebruikt een dynamische, interface-gedreven architectuur waarbij pre-configuratie checks en job pipelines worden bepaald op basis van de DestinationName uit de tenant sync settings. Dit maakt het mogelijk om verschillende bestemmingen (zoals Entra AD en Entra) te ondersteunen met elk hun eigen checks en job stappen.

Architectuur

SetTenantId

Leest DestinationName

DestinationName

Entra AD

Entra

Alle checks OK

Check gefaald: Disable

Check gefaald: Fail

IamEmployeePreConfigurationCheckJob

ISyncPipelineResolver

IAM_Employee_Sync_Settings

Keyed DI Lookup

HrToEntraAdPipelineDefinition

HrToEntraPipelineDefinition

PreConfigurationCheckRunner

Pipeline Jobs Starten

DisableSyncJob

Exception + Stop

Resolutie Flow

  1. Job start en zet tenant context via IMultiTenantService

  2. ISyncPipelineResolver leest DestinationName uit tenant settings

  3. Resolver zoekt de juiste ISyncPipelineDefinition via keyed DI

  4. PreConfigurationCheckRunner voert alle checks van de pipeline uit

  5. Bij succes: pipeline jobs worden aan de Hangfire batch gekoppeld

  6. Bij falen: DisableSyncJob of exception, afhankelijk van het failure type

Pipeline Definities

HR --> Entra AD (Hybrid Identity)

Key: "Entra AD" | Class: HrToEntraAdPipelineDefinition

Voor tenants die gebruik maken van een on-premises provisioning agent (Entra Connect Sync of Cloud Sync).

Pre-configuratie Checks:

Check

Volgorde

Failure Type

Beschrijving

ProvisioningSchemaCheck

10

Fail

Kan het provisioning schema opgehaald worden?

ProvisioningCredentialsCheck

20

Fail

Zijn de API credentials geldig?

ProvisioningJobActiveCheck

30

Disable

Is de provisioning job actief in Azure?

SchemaHashPresentCheck

40

Disable

Is er een schema hash geconfigureerd?

SchemaHashMatchCheck

50

Disable

Komt de schema hash overeen met het live schema?

EmailConfigurationCheck

60

Disable

Is de email configuratie voor initial passwords compleet?

Job Pipeline (7 stappen):

GetHrData

TranslateHrData

SendToEntraProvisioning

ReadLogs

SetInitialPassword

UpdateFields

TriggerWorkflows

HR --> Entra (Cloud-only)

Key: "Entra" | Class: HrToEntraPipelineDefinition

Voor tenants die direct via Microsoft Graph API werken, zonder on-premises provisioning agent.

Pre-configuratie Checks:

Check

Volgorde

Failure Type

Beschrijving

EmailConfigurationCheck

60

Disable

Is de email configuratie voor initial passwords compleet?

Job Pipeline (6 stappen):

GetHrData

TranslateHrData

CalculateActions

BatchGraphRequests

ReadEntraChanges

TriggerWorkflows

Stap

Job

Status

Beschrijving

1

IamEmployeeGetHrDataJob

Bestaand

HR data ophalen uit source connector

2

IamEmployeeTranslateHrDataJob

Bestaand

Data transformeren en valideren

3

IamEmployeeCalculateActionsJob

In ontwikkeling

Acties bepalen: aanmaken, wijzigen, deactiveren

4

IamEmployeeBatchGraphRequestsJob

In ontwikkeling

Acties uitvoeren via Microsoft Graph API batch endpoint

5

IamEmployeeReadEntraChangesJob

Placeholder

Verifieer welke wijzigingen succesvol zijn toegepast in Entra

6

IamEmployeeTriggerWorkflowsJob

Bestaand

Workflows triggeren voor verwerkte medewerkers

Pre-configuratie Check Systeem

Interface: IPreConfigurationCheck

Elke check implementeert IPreConfigurationCheck met de volgende eigenschappen:

Eigenschap

Type

Beschrijving

Name

string

Unieke naam voor logging en persistentie

Order

int

Uitvoeringsvolgorde (laag = eerst)

FailurePriority

int

Prioriteit bij meerdere failures (laag = wint)

AuditLabel

string

Label voor audit log entries

ExecuteAsync()

method

Voert de check uit en retourneert een PreCheckOutcome

Alle check implementaties zijn internal sealed classes - ze zijn niet bedoeld voor inheritance en zijn alleen toegankelijk binnen de AutoMate.API assembly.

PreCheckConstants

Alle magic strings en numbers worden centraal beheerd in PreCheckConstants:

Nested Class

Doel

Voorbeelden

ContextKeys

Keys voor PreCheckContext

ProvisioningSchema, ProvisioningJobActive

AuditStatus

Audit status strings

PreCheckFailed, FinishedHashMismatch

Order

Uitvoeringsvolgorde constanten

ProvisioningSchema = 10, EmailConfiguration = 60

FailurePriority

Failure prioriteit constanten

ProvisioningJobNotActive = 10, ApiIssues = 50

Dit voorkomt typo's en maakt refactoring eenvoudiger.

Gedeelde Context

Checks communiceren via PreCheckContext - een shared state container:

ProvisioningSchemaCheck --schrijft--> ContextKeys.ProvisioningSchema (ScimAdSchema) ProvisioningJobActiveCheck --schrijft--> ContextKeys.ProvisioningJobActive (bool) SchemaHashPresentCheck --schrijft--> ContextKeys.ExistingSchemaHash (string) | SchemaHashMatchCheck --leest alle drie------+

Checks draaien sequentieel (op volgorde van Order) omdat latere checks afhankelijk kunnen zijn van de context die eerdere checks schrijven.

PreCheckOutcome Factory Methods

PreCheckOutcome biedt factory methods voor leesbaarheid:

Method

Gebruik

Success(string)

Check geslaagd

Failure(string, CheckFailureAction)

Check gefaald met actie

Skipped(string)

Check overgeslagen (telt als geslaagd)

Failure Prioriteit

Wanneer meerdere checks falen, bepaalt FailurePriority welke actie wordt genomen:

Prioriteit

Check

Actie

10

ProvisioningJobActive

Disable sync

20

SchemaHashPresent

Disable sync

21

SchemaHashMatch

Disable sync

30

EmailConfiguration

Disable sync

50

ProvisioningSchema / Credentials

Fail (exception)

Disable = batch annuleren + DisableSyncJob schedulen Fail = batch als failed markeren + exception gooien

Medewerker Draft Validatie

Voordat een EmployeeDraft de provisioning pipeline in gaat, valideert EmployeeDraftValidationService of alle verplichte velden aanwezig zijn. Deze stap vindt plaats aan het einde van de GetHrData-fase, direct nadat de bron-connector de JSONata-transformatie heeft uitgevoerd.

Positie in de pipeline

Alle velden aanwezig

Ontbrekende velden

HR bron ophalen

JSONata schema toepassen

EmployeeDraftValidationService.Validate

Employee object aanmaken

MappingFailure toevoegen aan resultaat

Provisioning pipeline

De validatie wordt aangeroepen vanuit de connector-implementaties (AfasConnector, SapSuccessFactorsConnector) als laatste stap van GetEmployeesAsync. Het geretourneerde SourceFetchResult<Employee> bevat zowel succesvol gevalideerde Employee-objecten als de MappingFailure-records voor afgewezen drafts.

Twee-laags verplicht-velden model

EmployeeDraftValidationService combineert twee categorieën verplichte velden via BuildEffectiveRequiredFields:

Laag

Velden

Configureerbaar

Baseline (hardcoded)

Username, Email, DisplayName

Nee — altijd vereist, ongeacht tenant-instellingen

Tenant-configuratie

Nul of meer extra velden

Ja — opgeslagen als RequiredFieldsJson in IAM_Employee_Sync_Settings

De baseline-velden staan als statische constante in de service:

private static readonly string[] BaselineRequired = ["Username", "Email", "DisplayName"];

De tenant-specifieke velden worden door de connector uitgelezen uit de sync settings en meegegeven als configuredRequiredFields:

// In AfasConnector.GetEmployeesAsync var configuredRequired = ParseRequiredFields(settings.RequiredFieldsJson); return validationService.Validate(draftMap, configuredRequired);

BuildEffectiveRequiredFields voegt de geconfigureerde velden toe aan de baseline, waarbij duplicaten (case-insensitive) worden overgeslagen:

private static List<string> BuildEffectiveRequiredFields(IReadOnlyList<string>? configured) { var effective = new List<string>(BaselineRequired); if (configured is null) return effective; foreach (var field in configured) { if (!effective.Any(e => string.Equals(e, field, StringComparison.OrdinalIgnoreCase))) effective.Add(field); } return effective; }

Gedrag bij validatiefouten

Een draft die één of meer verplichte velden mist, wordt niet omgezet naar een Employee-object. In plaats daarvan wordt een MappingFailure aangemaakt met:

  • De identifier van de draft (voorkeursvolgorde: UsernameEmailDisplayName"Unknown")

  • Een foutmelding in het formaat Missing minimum(s): <veld1>, <veld2>

  • De index van de draft in het batch-resultaat

Deze failures worden doorgegeven aan het SourceFetchResult en zijn zichtbaar in de sync-logs. Afgewezen drafts worden niet verwerkt door de downstream pipeline-stappen.

Tenant-configuratie van extra verplichte velden

De tenant-specifieke verplichte velden worden beheerd via IAM_Employee_Sync_Settings.AdditionalRequiredFields en opgeslagen als JSON in de kolom RequiredFieldsJson. De EmployeeSyncSettingsService serialiseert en deserialiseert deze lijst bij het opslaan en ophalen van settings.

Interface

public interface IEmployeeDraftValidationService { SourceFetchResult<Employee> Validate( MappingBatchResult<EmployeeDraft> draftResult, IReadOnlyList<string>? configuredRequiredFields = null); }

De service is geregistreerd als Scoped via DI en wordt geïnjecteerd in elke connector die ISourceConnector implementeert.

Testdekking

EmployeeDraftValidationService is volledig unit-getest in EmployeeDraftValidationServiceTests met 36+ scenario's, waaronder:

  • Baseline validatie (ontbrekende Username, Email, DisplayName)

  • Tenant-specifieke extra velden (aanwezig, afwezig, case-insensitieve matching)

  • Deduplicatie van baseline- en geconfigureerde velden

  • Identifier-selectie bij gedeeltelijk ontbrekende gegevens

  • Doorsturen van bestaande failures uit de draft-mappingfase

TriggerWorkflows - Configureerbare Data Bronnen

De IamEmployeeTriggerWorkflowsJob accepteert parameters die aangeven uit welke voorgaande stap de user data gelezen moet worden:

Pipeline

Primary Step

Secondary Step

Entra AD

"ReadLogs"

"UpdateFields"

Entra

"IamEmployeeReadEntraChanges"

null

De Entra AD pipeline gebruikt de backward-compatible 3-parameter RunAsync(tenantId, batchId, pipelineTag) die standaard "ReadLogs" en "UpdateFields" gebruikt. De Entra pipeline gebruikt de 5-parameter variant: RunAsync(tenantId, batchId, pipelineTag, "IamEmployeeReadEntraChanges", null).

Beide pipelines gebruiken nu een gestandaardiseerd formaat (ReadLogsResultData) voor de output van hun respectievelijke "read changes" stappen, wat zorgt voor consistente workflow triggers.

DI Registratie

Alle componenten worden geregistreerd in Program.cs:

// Individuele checks (herbruikbaar over pipelines) builder.Services .AddScoped<ProvisioningSchemaCheck>() .AddScoped<ProvisioningCredentialsCheck>() .AddScoped<ProvisioningJobActiveCheck>() .AddScoped<SchemaHashPresentCheck>() .AddScoped<SchemaHashMatchCheck>() .AddScoped<EmailConfigurationCheck>() // Pipeline definities (keyed op DestinationName) .AddKeyedScoped<ISyncPipelineDefinition, HrToEntraAdPipelineDefinition>("Entra AD") .AddKeyedScoped<ISyncPipelineDefinition, HrToEntraPipelineDefinition>("Entra") // Pipeline resolver en check runner .AddScoped<ISyncPipelineResolver, SyncPipelineResolver>() .AddScoped<PreConfigurationCheckRunner>();

De keyed service key komt exact overeen met de DestinationName string in IAM_Employee_Sync_Settings.

Nieuwe Pipeline Toevoegen

Stap 1: Pipeline Definitie

Maak een nieuwe class die ISyncPipelineDefinition implementeert:

/// <summary> /// Pipeline definition for syncing HR data to new destination. /// Executes checks in priority order before starting the processing batch. /// </summary> internal sealed class HrToNieuwePipelineDefinition( EmailConfigurationCheck emailCheck, /* eventuele nieuwe checks */) : ISyncPipelineDefinition { public string Tag => "HR --> Nieuwe Bestemming Processing Batch"; /// <summary> /// Checks execute in Order sequence: /// 1. Email configuration (60) /// 2. ... (andere checks) /// </summary> public IReadOnlyList<IPreConfigurationCheck> Checks { get; } = [emailCheck]; public void AttachPipelineJobs(string batchId, Guid tenantId, string pipelineTag) { BatchJob.Attach(batchId, b => { // Definieer je job keten hier }); } }

Design Guidelines:

  • internal sealed: Pipeline definities zijn implementation details

  • XML docs: Documenteer het doel en de check volgorde

  • Constructor injection: Inject alle benodigde checks via DI

Stap 2: DI Registratie

Voeg toe aan Program.cs:

.AddKeyedScoped<ISyncPipelineDefinition, HrToNieuwePipelineDefinition>("NieuweDestinatie")

Stap 3: Tenant Configuratie

Stel DestinationName = "NieuweDestinatie" in de tenant sync settings.

Nieuwe Check Toevoegen

Stap 1: Constanten toevoegen aan PreCheckConstants

Voeg eerst de benodigde constanten toe:

// In PreCheckConstants.cs public static class Order { // ... bestaande constanten public const int MijnNieuweCheck = 70; // Na bestaande checks } public static class FailurePriority { // ... bestaande constanten public const int MijnCheckIssue = 40; // Prioriteit bij failure } public static class AuditStatus { // ... bestaande constanten public const string FinishedMijnCheckFailed = "Finished:PreCheck-MijnCheckFailed"; } public static class ContextKeys // Als je context data deelt { // ... bestaande constanten public const string MijnCheckData = nameof(MijnCheckData); }

Stap 2: Check Class

internal sealed class MijnNieuweCheck(/* dependencies */) : IPreConfigurationCheck { public string Name => nameof(MijnNieuweCheck).Replace("Check", string.Empty); public int Order => PreCheckConstants.Order.MijnNieuweCheck; public int FailurePriority => PreCheckConstants.FailurePriority.MijnCheckIssue; public string AuditLabel => "Mijn Nieuwe Check"; public async Task<PreCheckOutcome> ExecuteAsync(PreCheckContext context, CancellationToken ct) { // Eventueel context data lezen var someData = context.Get<SomeType>(PreCheckConstants.ContextKeys.SomeData); var passed = /* validatie logica */; if (!passed) return CreateFailure(); // Eventueel context data schrijven voor volgende checks context.Set(PreCheckConstants.ContextKeys.MijnCheckData, resultData); return PreCheckOutcome.Success("OK"); } private static PreCheckOutcome CreateFailure() => PreCheckOutcome.Failure( "Niet OK", new CheckFailureAction( CheckFailureType.Disable, "Reden voor disable", PreCheckConstants.AuditStatus.FinishedMijnCheckFailed, "Gebruikersvriendelijke foutmelding")); }

Design Guidelines:

  • internal sealed: Voorkomt inheritance en beperkt visibility tot assembly

  • nameof() voor Name: Voorkomt typo's en ondersteunt refactoring

  • Gebruik constanten: Voor Order, FailurePriority, AuditStatus, ContextKeys

  • Factory methods: Gebruik PreCheckOutcome.Success(), Failure(), Skipped()

  • Helper methods: Extract herhaalde CheckFailureAction creatie naar private methods

Stap 3: DI Registratie

.AddScoped<MijnNieuweCheck>()

Stap 4: Toevoegen aan Pipeline

Inject de check in de pipeline definitie constructor en voeg toe aan de Checks lijst:

internal sealed class HrToEntraAdPipelineDefinition( ProvisioningSchemaCheck schemaCheck, MijnNieuweCheck mijnCheck) : ISyncPipelineDefinition { public IReadOnlyList<IPreConfigurationCheck> Checks { get; } = [schemaCheck, ..., mijnCheck]; // Volgorde wordt bepaald door Order property }

Folder Structuur

Features/IAM/Jobs/ ├── Shared/ # Pipeline steps gebruikt door beide pipelines │ ├── IamEmployeeGetHrDataJob.cs # Stap 1: HR data ophalen │ ├── IamEmployeeTranslateHrDataJob.cs # Stap 2: Data transformeren en valideren │ └── IamEmployeeTriggerWorkflowsJob.cs # Laatste stap: Workflows triggeren │ ├── Orchestration/ # Jobs die andere jobs schedulen/orchestreren │ ├── IamEmployeeScheduleDispatcherJob.cs # Schedules terugkerende sync runs │ └── IamEmployeePreConfigurationCheckJob.cs # Orchestreert pre-checks voor pipeline │ ├── Management/ # Jobs die het sync proces beheren/controleren │ ├── IamEmployeeDisableSyncJob.cs # Schakelt sync uit bij validatie fouten │ └── IamEmployeeCancelFailedJobsAfterRetriesExhaustedJob.cs # Cleanup na retries │ ├── Monitoring/ # Jobs voor metrics en monitoring │ └── IamMetricsJobs.cs # Verzamelt sync metrics │ ├── Features/ # Andere IAM features (niet employee sync) │ └── SyncSharedMailboxesJob.cs # Shared mailbox sync feature │ ├── EntraAd/ # Entra AD (hybrid) pipeline-specifieke jobs │ ├── IamEmployeeSendToEntraProvisioningJob.cs # Stap 3: Verstuur naar provisioning API │ ├── IamEmployeeReadLogsJob.cs # Stap 4: Lees provisioning logs │ ├── IamEmployeeSetInitialPasswordJob.cs # Stap 5: Zet initiële wachtwoorden │ └── IamEmployeeUpdateFieldsJob.cs # Stap 6: Update extra velden │ ├── Entra/ # Entra (cloud-only) pipeline-specifieke jobs │ ├── IamEmployeeCalculateActionsJob.cs # Stap 3: Bepaal acties (create/update/disable) │ ├── IamEmployeeBatchGraphRequestsJob.cs # Stap 4: Voer batch Graph API requests uit │ └── IamEmployeeReadEntraChangesJob.cs # Stap 5: Verifieer toegepaste wijzigingen │ ├── Models/ # Shared models voor job results │ ├── ReadLogsResultData.cs # Canonical DTO voor ReadLogs/ReadEntraChanges │ └── ... (andere shared models) │ └── PreConfigurationChecks/ ├── IPreConfigurationCheck.cs # Check interface (public) ├── PreCheckContext.cs # Gedeelde context tussen checks (sealed) ├── PreCheckOutcome.cs # Result modellen en enums (sealed records) ├── PreCheckConstants.cs # Centrale constanten voor checks ├── PreConfigurationCheckRunner.cs # Pipeline-agnostische orchestrator (sealed) ├── Checks/ │ ├── ProvisioningSchemaCheck.cs # Entra AD specifiek (internal sealed) │ ├── ProvisioningCredentialsCheck.cs # Entra AD specifiek (internal sealed) │ ├── ProvisioningJobActiveCheck.cs # Entra AD specifiek (internal sealed) │ ├── SchemaHashPresentCheck.cs # Entra AD specifiek (internal sealed) │ ├── SchemaHashMatchCheck.cs # Entra AD specifiek (internal sealed) │ └── EmailConfigurationCheck.cs # Gedeeld over pipelines (internal sealed) └── Pipelines/ ├── ISyncPipelineDefinition.cs # Pipeline interface (public) ├── ISyncPipelineResolver.cs # Resolver interface (public) ├── SyncPipelineResolver.cs # Resolver: settings -> pipeline (internal sealed) ├── HrToEntraAdPipelineDefinition.cs # Pipeline: HR -> Entra AD (internal sealed) └── HrToEntraPipelineDefinition.cs # Pipeline: HR -> Entra Cloud (internal sealed)

Gerelateerde Pagina's

Last modified: 12 May 2026