Public/Get-UserProvisioningSyncSessionOperation.ps1
|
function Get-UserProvisioningSyncSessionOperation { [CmdletBinding()] param () Begin { Write-ConnectorVerbose "Getting all users from all included OUs..." $Script:UserMap = @{} $Script:IncludedOUs | ForEach-Object { Write-ConnectorVerbose "Getting users from OU: $_" Get-ADUser -SearchBase $_ -LDAPFilter "($($Script:LifeCycleStateAttribute)=*)" -Properties * | ForEach-Object { $State = $_.($Script:LifeCycleStateAttribute) | ConvertFrom-Json if ($State.identifier) { if ($Script:UserMap.ContainsKey($State.identifier)) { Write-Warning "Multiple users with the same lifecycle state identifier '$($State.identifier)' found. This may lead to unexpected behavior. Please ensure that the attribute '$($Script:LifeCycleStateAttribute)' contains unique identifiers for each user." } $Script:UserMap[$State.identifier] = $_ } else { Write-warning "User $($_.DistinguishedName) does not have a valid lifecycle state in attribute '$($Script:LifeCycleStateAttribute)'. Skipping user." } } } Write-ConnectorVerbose "Total users with valid lifecycle state found in included OUs: $($Script:UserMap.Count)" } Process { $Script:SyncSessionObjects.GetEnumerator() | ForEach-Object { $Identifier = $_.Key $InputObject = $_.Value if ($Script:UserMap.ContainsKey($Identifier)) { $ExistingUser = $Script:UserMap[$Identifier] # Compare the existing user with the input object to determine if an update is needed $Parameters = @{} $InputObject.Keys | Foreach-Object { if ($_ -eq "manager") { if ($null -eq $InputObject.manager) { if ($ExistingUser.manager) { Write-Verbose "Manager differs for user with identifier '$Identifier'. Manager will be cleared." $Parameters["Clear"] ??= @() $Parameters["Clear"] += "manager" } } elseif ($Script:UserMap.ContainsKey($InputObject.manager)) { if ($Script:UserMap[$InputObject.manager].DistinguishedName -ne $ExistingUser.manager) { $Parameters["Replace"] ??= @{} $Parameters["Replace"][$_] = $Script:UserMap[$InputObject.manager].DistinguishedName } } else { Write-Warning "Manager with identifier '$($InputObject.manager)' not found in existing users. Manager will not be updated for user with identifier '$Identifier'." } } elseif ($_ -eq "enabled") { if ($InputObject.enabled -ne $ExistingUser.Enabled) { Write-Verbose "Enabled state differs for user with identifier '$Identifier'. Enabled will be updated to '$($InputObject.enabled)'." $Parameters["Enabled"] = $InputObject.enabled } } elseif ($ExistingUser.$_ -cne $InputObject.$_) { if ($null -eq $InputObject.$_) { $Parameters["Clear"] ??= @() $Parameters["Clear"] += $_ } else { $Parameters["Replace"] ??= @{} $Parameters["Replace"][$_] = $InputObject.$_ } } } $State = $ExistingUser."$($Script:LifeCycleStateAttribute)" | ConvertFrom-Json if ($State.disabled) { $State.disabled = $null $Parameters["Replace"] ??= @{} $Parameters["Replace"]["$($Script:LifeCycleStateAttribute)"] = $State | ConvertTo-Json -Compress } if ($Parameters.Count -gt 0) { Write-Verbose "User with identifier '$Identifier' exists but has differences. An update operation will be planned." New-UserProvisioningSyncSessionOperation -Action "Set-ADUser" -Identity $ExistingUser.ObjectGUID.ToString() -Parameters $Parameters } else { # User exists and is the same, no operation needed Write-Debug "User with identifier '$Identifier' already exists and is up to date. No operation will be planned." } } else { # User does not exist, create needed Write-Verbose "User with identifier '$Identifier' does not exist. A create operation will be planned." $Parameters = @{ OtherAttributes = @{} } $InputObject.Keys | ForEach-Object { if ($_ -eq "manager") { if ($InputObject.manager) { if ($Script:UserMap.ContainsKey($InputObject.manager)) { $Parameters["OtherAttributes"][$_] = $Script:UserMap[$InputObject.manager].DistinguishedName } else { Write-Warning "Manager with identifier '$($InputObject.manager)' not found in existing users. Manager will not be set for user with identifier '$Identifier'." } } } elseif ($_ -eq "enabled") { $Parameters["Enabled"] = $InputObject.enabled ?? $false } elseif ($_ -eq "ou") { $Parameters["Path"] = $InputObject.ou } elseif ($InputObject.$_) { $Parameters["OtherAttributes"][$_] = $InputObject.$_ } } $Parameters["OtherAttributes"][$Script:LifeCycleStateAttribute] = @{ identifier = $Identifier disabled = $null } | ConvertTo-Json -Compress if (!$Parameters.ContainsKey("SamAccountName")) { if (!$InputObject.givenName -or !$InputObject.sn) { Write-Warning "Cannot generate SamAccountName for user with identifier '$Identifier' because givenName or sn is missing." } else { Get-UserProvisioningSamAccountName -givenName $InputObject.givenName -sn $InputObject.sn | Where-Object { $s = $_ !(Get-ADUser -LDAPFilter "(sAMAccountName=$s)" -ErrorAction SilentlyContinue) } | Select-Object -First 1 | ForEach-Object { $Parameters["SamAccountName"] = $_ } if (!$Parameters["SamAccountName"]) { $Parameters["SamAccountName"] = (New-Guid).ToString().Replace("-", "").Substring(0, 20) } } } if (!$Parameters.ContainsKey("Name")) { if ($Parameters["SamAccountName"]) { $Parameters["Name"] = $Parameters["SamAccountName"] } else { $Parameters["Name"] = $Identifier } } if (!$Parameters.ContainsKey("Path")) { $Parameters["Path"] = $Script:DefaultDestinationOU } New-UserProvisioningSyncSessionOperation -Action "New-ADUser" -Identity $Identifier -Parameters $Parameters } } # Any remaining users in the map are not present in the input objects and should be scheduld by deletion, by: # Checking the life cycle attribute 'disabled' property # If the date is more than $Script:DeleteUsersAfterDays (set during connect) days ago - create a Remove-ADObject operation # Else if the 'disabled' time stamp is not there, create a Set-ADUser operation that updates the life cycle attribute with a 'disabled'timestamp to now() $Script:UserMap.GetEnumerator() | Where-Object { -not $Script:SyncSessionObjects.ContainsKey($_.Key) } | ForEach-Object { $Identifier = $_.Key $ExistingUser = $_.Value $State = $ExistingUser.($Script:LifeCycleStateAttribute) | ConvertFrom-Json if ($null -ne $State.disabled) { $DisabledDate = Get-Date $State.disabled if ($ExistingUser.Enabled) { Write-Warning "User with identifier '$Identifier' is enabled but has a disabled timestamp in the lifecycle state attribute. This is an inconsistent state. A disable operation will be planned to correct the inconsistency before any deletion scheduling is done." New-UserProvisioningSyncSessionOperation -Action "Set-ADUser" -Identity $ExistingUser.DistinguishedName -Parameters @{ Replace = @{ "$($Script:LifeCycleStateAttribute)" = @{ identifier = $Identifier disabled = (Get-Date).ToString("o") } | ConvertTo-Json -Compress } Enabled = $false } } elseif ($DisabledDate -lt (Get-Date).AddDays(-$Script:DeleteUsersAfterDays)) { # User has been disabled for longer than the threshold, schedule for deletion Write-Verbose "User with identifier '$Identifier' has been disabled since '$DisabledDate', which is longer than the threshold of '$($Script:DeleteUsersAfterDays)' days. A delete operation will be planned." New-UserProvisioningSyncSessionOperation -Action "Remove-ADObject" -Identity $ExistingUser.DistinguishedName } else { # User has been disabled but not long enough, no operation needed Write-Debug "User with identifier '$Identifier' has been disabled since '$DisabledDate', which is not longer than the threshold of '$($Script:DeleteUsersAfterDays)' days. No operation will be planned at this time." } } else { # User is not disabled, schedule for disablement by updating the life cycle state attribute with a 'disabled' timestamp to now() Write-Verbose "User with identifier '$Identifier' is not disabled but is missing from the input objects. A disable operation will be planned." New-UserProvisioningSyncSessionOperation -Action "Set-ADUser" -Identity $ExistingUser.DistinguishedName -Parameters @{ Replace = @{ "$($Script:LifeCycleStateAttribute)" = @{ identifier = $Identifier disabled = (Get-Date).ToString("o") } | ConvertTo-Json -Compress } Enabled = $false } } } } End { } } |