Public/New-IdleEntraConnectDirectorySyncProvider.ps1
|
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 a credential AuthSession provided by the host and establishes a PSRemoting session to the target Entra Connect server internally. 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, ComputerName, AuthSession), GetSyncCycleState(ComputerName, AuthSession) .EXAMPLE $provider = New-IdleEntraConnectDirectorySyncProvider $provider.GetCapabilities() # Returns: @('IdLE.DirectorySync.Trigger', 'IdLE.DirectorySync.Status') .EXAMPLE # With a credential from AuthSessionBroker (AuthSessionType='Credential') $credential = Get-Credential $provider = New-IdleEntraConnectDirectorySyncProvider $result = $provider.StartSyncCycle('Delta', 'ad-sync1.corp.local', $credential) #> [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 NewRemoteSession -Value { param( [Parameter(Mandatory)] [string] $ComputerName, [Parameter(Mandatory)] [pscredential] $Credential ) try { return New-PSSession -ComputerName $ComputerName -Credential $Credential -ErrorAction Stop } catch { throw "Failed to establish PSRemoting session to '$ComputerName': $($_.Exception.Message)" } } -Force $provider | Add-Member -MemberType ScriptMethod -Name InvokeRemoteCommand -Value { param( [Parameter(Mandatory)] [object] $Session, [Parameter(Mandatory)] [scriptblock] $ScriptBlock, [Parameter()] [AllowNull()] [object[]] $ArgumentList ) if ($null -eq $ArgumentList -or $ArgumentList.Count -eq 0) { return Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ErrorAction Stop } return Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ErrorAction Stop } -Force $provider | Add-Member -MemberType ScriptMethod -Name RemoveRemoteSession -Value { param( [Parameter(Mandatory)] [AllowNull()] [object] $Session ) if ($null -ne $Session) { try { Remove-PSSession -Session $Session -ErrorAction Stop } catch { Write-Warning "Failed to remove PSRemoting session: $($_.Exception.Message)" } } } -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 ComputerName Target Entra Connect server hostname for PSRemoting. .PARAMETER AuthSession Credential ([PSCredential]) provided by the host's AuthSessionBroker. .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)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $ComputerName, [Parameter(Mandatory)] [ValidateNotNull()] [object] $AuthSession ) if ($AuthSession -isnot [pscredential]) { $actualType = $AuthSession.GetType().FullName throw "AuthSession must be a [PSCredential] for PSRemoting session creation. Received: [$actualType]" } $remoteSession = $null try { $remoteSession = $this.NewRemoteSession($ComputerName, $AuthSession) $this.InvokeRemoteCommand($remoteSession, { param([string] $RemotePolicyType) Import-Module -Name ADSync -ErrorAction Stop Start-ADSyncSyncCycle -PolicyType $RemotePolicyType -ErrorAction Stop }, @($PolicyType)) | Out-Null return [pscustomobject]@{ Started = $true Message = "Sync cycle triggered with PolicyType: $PolicyType on $ComputerName" } } 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" } throw "Failed to start sync cycle: $errorMessage" } finally { $this.RemoveRemoteSession($remoteSession) } } -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 ComputerName Target Entra Connect server hostname for PSRemoting. .PARAMETER AuthSession Credential ([PSCredential]) provided by the host's AuthSessionBroker. .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)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $ComputerName, [Parameter(Mandatory)] [ValidateNotNull()] [object] $AuthSession ) if ($AuthSession -isnot [pscredential]) { $actualType = $AuthSession.GetType().FullName throw "AuthSession must be a [PSCredential] for PSRemoting session creation. Received: [$actualType]" } $remoteSession = $null try { $remoteSession = $this.NewRemoteSession($ComputerName, $AuthSession) $scheduler = $this.InvokeRemoteCommand($remoteSession, { Import-Module -Name ADSync -ErrorAction Stop Get-ADSyncScheduler -ErrorAction Stop }, @()) # 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" } finally { $this.RemoveRemoteSession($remoteSession) } } -Force return $provider } |