-
Notifications
You must be signed in to change notification settings - Fork 0
Add Entra Connect directory sync step pack and provider #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
2c13056
Initial plan
Copilot 14f0822
Add Entra Connect directory sync step pack and provider modules
Copilot 8e38eea
Add tests and example workflow for Entra Connect sync
Copilot 4f4fce4
Fix test helper and test implementations for DirectorySync step
Copilot b9bc72a
Regenerate step reference to include TriggerDirectorySync
Copilot 57c200d
Fix example comment in Get-IdleStepMetadataCatalog and regenerate docs
Copilot 9cdc944
Address PR feedback: change example to use Group entitlement and redu…
Copilot 599e75d
Update Generate-IdleStepReference.ps1 default modules to include Dire…
Copilot 7877f1f
Restore TriggerDirectorySync section in steps.md that was accidentall…
Copilot 513c0d3
Apply suggestions from code review
blindzero 7dbb5ce
Eliminate code duplication: consolidate helper functions and metadata…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| @{ | ||
| Name = 'Joiner - Trigger Entra Connect Sync' | ||
| LifecycleEvent = 'Joiner' | ||
| Steps = @( | ||
| @{ | ||
| Name = 'Create AD account' | ||
| Type = 'IdLE.Step.CreateIdentity' | ||
| With = @{ | ||
| IdentityKey = '{{ Request.Username }}' | ||
| Attributes = @{ | ||
| GivenName = '{{ Request.GivenName }}' | ||
| Surname = '{{ Request.Surname }}' | ||
| Department = '{{ Request.Department }}' | ||
| } | ||
| AuthSessionName = 'SourceAD' | ||
| Provider = 'Identity' | ||
| } | ||
| } | ||
| @{ | ||
| Name = 'Trigger Entra Connect Delta Sync' | ||
| Type = 'IdLE.Step.TriggerDirectorySync' | ||
| With = @{ | ||
| AuthSessionName = 'EntraConnect' | ||
| AuthSessionOptions = @{ | ||
| Role = 'EntraConnectAdmin' | ||
| } | ||
| PolicyType = 'Delta' | ||
| Wait = $true | ||
| TimeoutSeconds = 300 | ||
| PollIntervalSeconds = 10 | ||
| Provider = 'DirectorySync' | ||
| } | ||
| } | ||
| @{ | ||
| Name = 'Assign Entra ID group membership' | ||
| Type = 'IdLE.Step.EnsureEntitlement' | ||
| With = @{ | ||
| IdentityKey = '{{ Request.Username }}' | ||
| Entitlement = @{ | ||
| Kind = 'Group' | ||
| Id = 'EntraID-Users-Group' | ||
| DisplayName = 'Entra ID Users' | ||
| } | ||
| State = 'Present' | ||
| AuthSessionName = 'EntraID' | ||
| Provider = 'Cloud' | ||
| } | ||
| } | ||
| ) | ||
| } |
22 changes: 22 additions & 0 deletions
22
src/IdLE.Provider.DirectorySync.EntraConnect/IdLE.Provider.DirectorySync.EntraConnect.psd1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| @{ | ||
| RootModule = 'IdLE.Provider.DirectorySync.EntraConnect.psm1' | ||
| ModuleVersion = '0.8.0' | ||
| GUID = 'a1b2c3d4-5e6f-7890-abcd-ef1234567890' | ||
| Author = 'Matthias Fleschuetz' | ||
| Copyright = '(c) Matthias Fleschuetz. All rights reserved.' | ||
| Description = 'Entra Connect directory sync provider for IdLE (remote execution).' | ||
| PowerShellVersion = '7.0' | ||
|
|
||
| FunctionsToExport = @( | ||
| 'New-IdleEntraConnectDirectorySyncProvider' | ||
| ) | ||
|
|
||
| PrivateData = @{ | ||
| PSData = @{ | ||
| Tags = @('IdentityLifecycleEngine', 'IdLE', 'Provider', 'DirectorySync', 'EntraConnect') | ||
| LicenseUri = 'https://www.apache.org/licenses/LICENSE-2.0' | ||
| ProjectUri = 'https://github.com/blindzero/IdentityLifecycleEngine' | ||
| ContactEmail = '13959569+blindzero@users.noreply.github.com' | ||
| } | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
src/IdLE.Provider.DirectorySync.EntraConnect/IdLE.Provider.DirectorySync.EntraConnect.psm1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| #requires -Version 7.0 | ||
| Set-StrictMode -Version Latest | ||
|
|
||
| $PublicPath = Join-Path -Path $PSScriptRoot -ChildPath 'Public' | ||
| if (Test-Path -Path $PublicPath) { | ||
|
|
||
| # Materialize the list first to avoid enumeration issues if the session/module state changes during import. | ||
| $publicScripts = @(Get-ChildItem -Path $PublicPath -Filter '*.ps1' -File | Sort-Object -Property FullName) | ||
|
|
||
| foreach ($script in $publicScripts) { | ||
| . $script.FullName | ||
| } | ||
| } | ||
|
|
||
| Export-ModuleMember -Function @( | ||
| 'New-IdleEntraConnectDirectorySyncProvider' | ||
| ) |
201 changes: 201 additions & 0 deletions
201
....Provider.DirectorySync.EntraConnect/Public/New-IdleEntraConnectDirectorySyncProvider.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| function New-IdleEntraConnectDirectorySyncProvider { | ||
| <# | ||
| .SYNOPSIS | ||
| Creates an Entra Connect directory sync provider for IdLE. | ||
|
|
||
| .DESCRIPTION | ||
| This provider triggers and monitors Entra ID Connect (ADSync) sync cycles on an | ||
| on-premises server via remote execution. | ||
|
|
||
| The provider uses an AuthSession object (remote execution handle) provided by the host. | ||
| The AuthSession must implement InvokeCommand(CommandName, Parameters) to execute | ||
| commands in an elevated/privileged context on the Entra Connect server. | ||
|
|
||
| No interactive prompts are made; elevation and authentication are the host's responsibility | ||
| via the AuthSessionBroker. | ||
|
|
||
| .OUTPUTS | ||
| PSCustomObject | ||
| Provider instance with methods: GetCapabilities(), StartSyncCycle(PolicyType, AuthSession), GetSyncCycleState(AuthSession) | ||
|
|
||
| .EXAMPLE | ||
| $provider = New-IdleEntraConnectDirectorySyncProvider | ||
| $provider.GetCapabilities() | ||
| # Returns: @('IdLE.DirectorySync.Trigger', 'IdLE.DirectorySync.Status') | ||
|
|
||
| .EXAMPLE | ||
| # With a mock remote execution handle | ||
| $mockAuthSession = [pscustomobject]@{ | ||
| InvokeCommand = { param($CommandName, $Parameters) | ||
| # Mock implementation | ||
| return @{ Started = $true } | ||
| } | ||
| } | ||
| $provider = New-IdleEntraConnectDirectorySyncProvider | ||
| $result = $provider.StartSyncCycle('Delta', $mockAuthSession) | ||
| #> | ||
| [CmdletBinding()] | ||
| param() | ||
|
|
||
| $provider = [pscustomobject]@{ | ||
| PSTypeName = 'IdLE.Provider.EntraConnectDirectorySync' | ||
| Name = 'EntraConnectDirectorySyncProvider' | ||
| } | ||
|
|
||
| $provider | Add-Member -MemberType ScriptMethod -Name GetCapabilities -Value { | ||
| <# | ||
| .SYNOPSIS | ||
| Advertises the capabilities provided by this provider instance. | ||
|
|
||
| .DESCRIPTION | ||
| Capabilities are stable string identifiers used by IdLE to validate that | ||
| a workflow plan can be executed with the available providers. | ||
| #> | ||
|
|
||
| return @( | ||
| 'IdLE.DirectorySync.Trigger' | ||
| 'IdLE.DirectorySync.Status' | ||
| ) | ||
| } -Force | ||
|
|
||
| $provider | Add-Member -MemberType ScriptMethod -Name StartSyncCycle -Value { | ||
| <# | ||
| .SYNOPSIS | ||
| Triggers an Entra Connect sync cycle. | ||
|
|
||
| .DESCRIPTION | ||
| Triggers a sync cycle via Start-ADSyncSyncCycle on the remote Entra Connect server. | ||
|
|
||
| .PARAMETER PolicyType | ||
| The sync policy type: 'Delta' or 'Initial'. | ||
|
|
||
| .PARAMETER AuthSession | ||
| Remote execution handle provided by the host's AuthSessionBroker. | ||
| Must implement InvokeCommand(CommandName, Parameters). | ||
|
|
||
| .OUTPUTS | ||
| PSCustomObject with properties: | ||
| - Started (bool): indicates whether the sync cycle was triggered | ||
| - Message (string, optional): additional information | ||
| #> | ||
| param( | ||
| [Parameter(Mandatory)] | ||
| [ValidateSet('Delta', 'Initial', IgnoreCase = $true)] | ||
| [string] $PolicyType, | ||
|
|
||
| [Parameter(Mandatory)] | ||
| [ValidateNotNull()] | ||
| [object] $AuthSession | ||
| ) | ||
|
|
||
| # Validate AuthSession contract | ||
| if ($null -eq $AuthSession.PSObject.Methods['InvokeCommand']) { | ||
| throw "AuthSession must implement InvokeCommand(CommandName, Parameters) method. " + | ||
| "The host must provide an elevated remote session via AuthSessionBroker." | ||
| } | ||
|
|
||
| try { | ||
| # Execute Start-ADSyncSyncCycle remotely | ||
| # The remote session should already have ADSync module available or will import it | ||
| $result = $AuthSession.InvokeCommand('Start-ADSyncSyncCycle', @{ | ||
| PolicyType = $PolicyType | ||
| }) | ||
|
|
||
| # Start-ADSyncSyncCycle returns a result object or throws on error | ||
| # Success case: return Started = true | ||
| return [pscustomobject]@{ | ||
| Started = $true | ||
| Message = "Sync cycle triggered with PolicyType: $PolicyType" | ||
| } | ||
| } | ||
| catch { | ||
| # Check for common privilege/elevation errors | ||
| $errorMessage = $_.Exception.Message | ||
|
|
||
| if ($errorMessage -match 'access.*denied|permission|privilege|elevation|administrator|unauthorized') { | ||
| throw "Failed to start sync cycle. Missing privileges or elevation. " + | ||
| "The AuthSession must provide an elevated execution context. Original error: $errorMessage" | ||
| } | ||
|
|
||
| # Re-throw other errors | ||
| throw "Failed to start sync cycle: $errorMessage" | ||
| } | ||
| } -Force | ||
|
|
||
| $provider | Add-Member -MemberType ScriptMethod -Name GetSyncCycleState -Value { | ||
| <# | ||
| .SYNOPSIS | ||
| Retrieves the current state of Entra Connect sync cycles. | ||
|
|
||
| .DESCRIPTION | ||
| Queries the sync scheduler state via Get-ADSyncScheduler to determine if a | ||
| sync cycle is currently in progress. | ||
|
|
||
| .PARAMETER AuthSession | ||
| Remote execution handle provided by the host's AuthSessionBroker. | ||
| Must implement InvokeCommand(CommandName, Parameters). | ||
|
|
||
| .OUTPUTS | ||
| PSCustomObject with properties: | ||
| - InProgress (bool): indicates whether a sync cycle is currently running | ||
| - State (string): 'InProgress', 'Idle', or 'Unknown' | ||
| - Details (hashtable, optional): additional state information | ||
| #> | ||
| param( | ||
| [Parameter(Mandatory)] | ||
| [ValidateNotNull()] | ||
| [object] $AuthSession | ||
| ) | ||
|
|
||
| # Validate AuthSession contract | ||
| if ($null -eq $AuthSession.PSObject.Methods['InvokeCommand']) { | ||
| throw "AuthSession must implement InvokeCommand(CommandName, Parameters) method. " + | ||
| "The host must provide an elevated remote session via AuthSessionBroker." | ||
| } | ||
|
|
||
| try { | ||
| # Execute Get-ADSyncScheduler remotely | ||
| $scheduler = $AuthSession.InvokeCommand('Get-ADSyncScheduler', @{}) | ||
|
|
||
| # Determine if sync is in progress | ||
| # Get-ADSyncScheduler returns an object with SyncCycleInProgress property | ||
| $inProgress = $false | ||
| $state = 'Unknown' | ||
| $details = @{} | ||
|
|
||
| if ($null -ne $scheduler) { | ||
| # Extract relevant properties | ||
| if ($scheduler.PSObject.Properties.Name -contains 'SyncCycleInProgress') { | ||
| $inProgress = [bool]$scheduler.SyncCycleInProgress | ||
| $state = if ($inProgress) { 'InProgress' } else { 'Idle' } | ||
| } | ||
|
|
||
| # Capture additional details for diagnostics | ||
| if ($scheduler.PSObject.Properties.Name -contains 'AllowedSyncCycleInterval') { | ||
| $details['AllowedSyncCycleInterval'] = $scheduler.AllowedSyncCycleInterval | ||
| } | ||
| if ($scheduler.PSObject.Properties.Name -contains 'NextSyncCyclePolicyType') { | ||
| $details['NextSyncCyclePolicyType'] = $scheduler.NextSyncCyclePolicyType | ||
| } | ||
| } | ||
|
|
||
| return [pscustomobject]@{ | ||
| InProgress = $inProgress | ||
| State = $state | ||
| Details = $details | ||
| } | ||
| } | ||
| catch { | ||
| $errorMessage = $_.Exception.Message | ||
|
|
||
| if ($errorMessage -match 'access.*denied|permission|privilege|elevation|administrator|unauthorized') { | ||
| throw "Failed to get sync cycle state. Missing privileges or elevation. " + | ||
| "The AuthSession must provide an elevated execution context. Original error: $errorMessage" | ||
| } | ||
|
|
||
| throw "Failed to get sync cycle state: $errorMessage" | ||
| } | ||
| } -Force | ||
|
|
||
| return $provider | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.