NEEDED: SOAP API call/method to "Initialize a Server Sync" to Cerberus Servers in Highly Available VM Set.
Intro:
I have been extensively scripting with Cerberus's SOAP API and Powershell .Net Methods, and I have to say; Bravo on the implementation. I have created many Powershell scheduled tasks using the Soap API for Account imports, folder mappings, and Cerberus Log Security Audits for our company's implementation of Cerberus Enterprise.
I have looked through the Soap API documentation extensively, pulled the latest methods from the Wsdl with PowerShell, and cannot find a direct way to initialize a server sync to other servers from the API methods.
Background:
2 Azure VMs running Cerberus SFTP v11.2.2 in an Azure VM Availability Set behind public Azure Load Balancer for server High Availability.
We are using an AD service account to perform SMB mappings to an Azure Files Share for our data storage. This was an issue I requested help on previously via email support, and have since solved. Thank you Dana Anderson!
Use Case:
We need a way to ensure password changes by user accounts we manage are programatically synced from the active server to the nonactive server in the Load Balancer Array, on demand. At this time we have configured a bi-directional auto sync every 15 minutes on both servers.
I can identify and filter on "Password changed for user account " in the Cerberus Logs with my current PowerShell script in testing but need a follow on Sync API Call to complete this script.
Problem:
In our testing of the above configuration and use case, a user would perform a password sync on the active server they are routed to in the load balancer array. That new password will not get synced to the other server immediately, and in most cases, the auto sync from the nonactive server can overwrite the new password change initiated by the active server the password change request originated from.
Conclusion:
Thank For your help and I can't wait until I see this in release notes.
Please let me know if i have overlooked any methods currently implemented in the Latest versions that can perform this task.
POWERSHELL for ALL!
Phil Grant
-
Official comment
Hi Phil,
Glad you're enjoying the SOAP API. We have a lot of room to improve, of course, and your feedback is really valuable to us. So thank you!
Have you considered storing your Cerberus users in AD or LDAP? While the setup and maintenance are non-zero costs, they are robust and handle exactly this situation, where password changes must be replicated across instances. Cerberus has good (and improving) integration with these servers and might be a good long-term solution.
That being said, it is definitely possible to implement what you describe through SOAP API. After all, the SOAP API is how Cerberus FTP implement its Sync functionality.
In broad strokes, you could use `GetProfiles` and `SetProfiles` between the two cerberus instances to do a replication of users between servers. A finer-grained solution might be to use `GetUserInformation` and `AddUser` to replicate only the exact user who's changed their password during the event. Be sure to set `saveToDisk` during the call to `AddUser` to ensure the change gets flushed to the filesystem.There are pitfalls to be careful of if you choose this path. One could easily write event loops that would infinitely replicate users between the two Cerberus instances!
I will file an enhancement request for what you ask for (a way to trigger replication through SOAP API) as well as one that (I think) gets to the root of what you're after (fine-grained replication between sync servers). -
Vincent,
Thank you so much for you reply.
We had considered an AD/LDAPS implementation, but ran into many design issues for external Customer company accounts, which is our primary use case. Another issue we had was merging accounts previously created on 2 other obsolete SFTP systems, migrating the data, and keeping the naming conventions the same for compatibility of External Customers deployed machine configurations that exclusively access our SFTP(could be thousands of machines that someone would have to manually update the changes). The good news is most of the migration is done and Cerberus will be our only SFTP system moving forward.
I have used the GetUserInformation and AddUser Methods, but was unaware of the "Savetodisk" property on the .net object for building the AddUserRequest. I will begin testing that call. If I submit a AddUserRequest with no "password" property defined, IIRC, that would respond with failed user creation due to "password" is $Null. Is that a correct statement?
Your last statement hit the nail on the head, a way to get the Sync trigger in the wsdl and/or fine grained replication for a specific user object.
Again, Vincent, Thank you So much.
We really like the Cerberus SFTP Product, and it is miles ahead of other SFTP products we have used.
0 -
> If I submit a AddUserRequest with no "password" property defined, IIRC, that would respond with failed user creation due to "password" is $Null. Is that a correct statement?
You'll get some kind of error, yes. In my test environment an empty password fails the password policy test.For the replication use-case you're describing, the password should already be hashed in the User object returned by `GetUserInformation`. If the User object already has a hashed password, AddUser will update the user object as such. The user on the source Cerberus instance and the destination instance should have the same password at this point.
`AddUser` will detect if the User object's password is plain-text and do password policy evaluation before hashing and saving the user.
0 -
Vincent,
I have been testing the GetUserInformation Approach you mentioned and had some questions to further understand your guidance.
If I perform a GetUserInformation from the source Cerberus instance where the password change was performed by a user, then if I call the Savetodisk method on the source Cerberus instance, would that trigger the rehashing on the Cerberus destination instance as well? I read above and thought "it can't be that easy."
So I have been testing a Local Powershell Scriptblock to perform the GetUserInformation, save the User object as JSON, so I can pass the user object to a RemoteScriptblock with PSRemoting to ensure i can access "localhost" on the Cerberus destination instance, since the API is only listening on loopback of Cerberus destination instance. Once connected to the Cerberus destination API instance, build an AddUser request, bind the Imported user Object to the request as the User object class, add SaveToDisk method to the Request, then submit to Cerberus destination instance. The reason I opted to try a json object to pass, I found in earlier testing, PSremoting doesn't support custom types or classes(especially namespace objects I created with New-WebserviceProxy on the Cerberus source instance).
I still haven't gotten this approach to work in testing, any ideas? I have posted scrubbed testing functions below.
$Script:ErrorLogPath = "C:\Temp\LogCollection\SyncTest.log"
$AccountUserName="sftp.test5"
If($Env:COMPUTERNAME -eq "cerb-01"){
$LocalServerIpAddress="10.0.103.106"
$RemoteServerIpAddress="10.0.103.107"
$RemoteServerComputerName="cerb-01"
}
If($Env:COMPUTERNAME -eq "cerb-02"){
$LocalServerIpAddress="10.0.103.107"
$RemoteServerIpAddress="10.0.103.106"
$RemoteServerComputerName="cerb-02"
}#Order of operations
#Get-LocalServerUserObject
#Start PSSessiontoRemote Server
#Pass UserObject to Remote Scriptblock
#Set-RemoteServerUserObject#Local Server ScriptBlock
Function Get-LocalServerUserObject{
Param(
[String]$AccountUserName,
[String]$LocalServerIpAddress
)
Function Connect-CerberusSOAPService{
[CmdletBinding()]
Param(
[String]$Environment,
[String]$ServerIpAddress
)
####################################################
$VerbosePreference="Continue"
$ErrorActionPreference="Stop"
$Error.Clear()
####################################################
$CerberusFTPCredentialfile=$null
$CerberusSOAPService=$null
$Script:CerberusSOAPService=$null
$Script:CerberusSOAPRequestCredentials=$null
####################################################
# Environment Switch
####################################################
switch ($Environment) {
"InternalDev" {
$wsdlFile="https://$($ServerIpAddress):8808/wsdl/Cerberus.wsdl"
$SoapRequestURL="https://localhost:10001/service/CerberusSOAPService"
$CerberusFTPCredentialFile="C:\Users\pgrant\Documents\DEVCerberusSOAP.cred"
}
"InternalPROD" {
$wsdlFile="https://$($ServerIpAddress):8808/wsdl/Cerberus.wsdl"
$SoapRequestURL="https://localhost:10001/service/CerberusSOAPService"
$CerberusFTPCredentialFile="C:\Users\pgrant\Documents\PRODCerberusSOAP.cred"
}
}#end of Environment Switch Statement
####################################################
#Prepare TLS v1.2 and Credentials
####################################################
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
####################################################
#Create Web Service Object to SOAP Connection
####################################################
$CerberusSOAPService=New-WebServiceProxy -Uri $wsdlFile -Class CerberusFTP -Namespace CerberusSOAPService -Credential (Import-Clixml -Path $CerberusFTPCredentialFile)
####################################################
If($CerberusSOAPService){
$CerberusSOAPService.Url=$SoapRequestURL
$CerberusSOAPService.PreAuthenticate = $true;
$Script:CerberusSOAPService = $CerberusSOAPService
####################################################
#Create Request Credential Object
####################################################
$Script:CerberusSOAPRequestCredentials = New-Object -TypeName CerberusSOAPService.Credentials
$Script:CerberusSOAPRequestCredentials.user = (Import-Clixml -Path $CerberusFTPCredentialFile).UserName
$Script:CerberusSOAPRequestCredentials.password = (Import-Clixml -Path $CerberusFTPCredentialFile).GetNetworkCredential().Password
$Script:CerberusFTPCredentialObject=(Import-Clixml -Path $CerberusFTPCredentialFile)
####################################################
}
Else{
Return
}
}#End of Function Connect-CerberusSOAPServiceConnect-CerberusSOAPService -Environment "InternalPROD" -ServerIpAddress $LocalServerIpAddress -Verbose
$getuserrequest=$null
$getuserresponse=$null
####################################################
#Create Get User Information Request Object
####################################################
[CerberusSOAPService.GetUserInformationRequest] $getuserrequest = New-Object -TypeName CerberusSOAPService.GetUserInformationRequest
$getuserrequest.credentials = $Script:CerberusSOAPRequestCredentials
$getuserrequest.userName = $AccountUserName
####################################################
# Create Get User Information Response Object
####################################################
[CerberusSOAPService.GetUserInformationResponse] $getuserresponse = $Script:CerberusSOAPService.GetUserInformation($getuserrequest)
####################################################
$getuserresponse.result
$getuserresponse.message
$Script:UserObject=$getuserresponse.UserInformation
$Script:CerberusSOAPService.Dispose()
}#End of Function Get-LocalServerUserObjectFunction Set-RemoteServerUserObject{
Param(
$UserObject,
[String]$RemoteServerIpAddress,
[String]$RemoteServerComputerName,
[System.Management.Automation.PSCredential]$RemoteCerberusFTPCredential
)
$PSSession=$null
$PSSessionCredential=$null
$PSSessionCredential= Import-CLIXML "C:\Users\pgrant\Documents\PRODCerberusService.cred"
$PSSession=New-PSSession -ComputerName $RemoteServerComputerName -Credential $PSSessionCredential
#Remote Server ScriptBlock
$RemoteServerScriptBlock={
Param(
$UserObject,
[String]$RemoteServerIpAddress,
[System.Management.Automation.PSCredential]$RemoteCerberusFTPCredentialFile
)
Function Connect-RemoteCerberusSOAPService{
#Converted for Remote Use
[CmdletBinding()]
Param(
[String]$RemoteServerIpAddress,
[System.Management.Automation.PSCredential]$RemoteCerberusFTPCredentialObject
)
####################################################
$VerbosePreference="Continue"
$ErrorActionPreference="Stop"
$Error.Clear()
####################################################
$RemotewsdlFile="https://$($RemoteServerIpAddress):8808/wsdl/Cerberus.wsdl"
$RemoteSoapRequestURL="https://localhost:10001/service/CerberusSOAPService"
####################################################
#Prepare TLS v1.2 and Credentials
####################################################
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
####################################################
#Create Web Service Object to SOAP Connection
####################################################
$RemoteCerberusSOAPService=New-WebServiceProxy -Uri $RemotewsdlFile -Class CerberusFTP -Namespace CerberusSOAPService -Credential $RemoteCerberusFTPCredentialObject
####################################################
If($RemoteCerberusSOAPService){
$RemoteCerberusSOAPService.Url=$RemoteSoapRequestURL
$RemoteCerberusSOAPService.PreAuthenticate = $true;
$Script:RemoteCerberusSOAPService = $RemoteCerberusSOAPService
####################################################
#Create Request Credential Object
####################################################
$Script:RemoteCerberusSOAPRequestCredentials = New-Object -TypeName CerberusSOAPService.Credentials
$Script:RemoteSOAPRequestCredentials.user = $($RemoteCerberusFTPCredentialObject).UserName
$Script:RemoteSOAPRequestCredentials.password = $($RemoteCerberusFTPCredentialObject).GetNetworkCredential().Password
####################################################
}
Else{
Write-output "Cannot Connect to SOAP API"
Return
}
}#End of Function Connect-RemoteCerberusSOAPServiceConnect-RemoteCerberusSOAPService -RemoteServerIpAddress $RemoteServerIpAddress -RemoteCerberusFTPCredentialObject $RemoteCerberusFTPCredentialFile
$ImportedUserObject=$UserObject |ConvertFrom-Json
Write-output $ImportedUserObject
$UpdateUserRequest=$null
$UpdateUserResponse=$null####################################################
# Create new $UpdateUserRequest object
####################################################
[CerberusSOAPService.AddUserRequest] $UpdateUserRequest = New-Object -TypeName CerberusSOAPService.AddUserRequest
$UpdateUserRequest.credentials = $Script:RemoteCerberusSOAPRequestCredentials
####################################################
# Populate request object with new user object
####################################################
$RemoteCerberusUserObject= New-Object -TypeName CerberusSOAPService.User -ArgumentList $ImportedUserObject
$UpdateUserRequest.User = $RemoteCerberusUserObject
$UpdateUserRequest.saveToDisk
$UpdateUserRequest.saveToDiskSpecified = $true
####################################################
# Create new $UpdateUserResponse object
####################################################
[CerberusSOAPService.AddUserResponse] $UpdateUserResponse = $Script:RemoteCerberusSOAPService.AddUser($UpdateUserRequest)Write-Output $UpdateUserResponse.Result
Write-Output $UpdateUserResponse.Message
$Script:RemoteCerberusSOAPService.Dispose()
}#End of Remote ScriptBlockInvoke-Command -Session $PSSession -ScriptBlock $RemoteServerScriptBlock -ArgumentList $UserObject,$RemoteServerIpAddress,$RemoteCerberusFTPCredentialFile
Remove-PSSession -Session $PSSession
}#End of Function Set-RemoteServerUserObject
Get-LocalServerUserObject -AccountUserName $AccountUserName -LocalServerIpAddress $LocalServerIpAddress
$Script:UserObject=$Script:UserObject |ConvertTo-Json
Set-RemoteServerUserObject -UserObject $Script:UserObject -RemoteServerComputerName $RemoteServerComputerName -RemoteServerIpAddress $RemoteServerIpAddress -RemoteCerberusFTPCredential $Script:CerberusFTPCredentialObject0 -
Hi PGrant,
> If I perform a GetUserInformation from the source Cerberus instance where the password change was performed by a user, then if I call the Savetodisk method on the source Cerberus instance,
I think you mean destination here, right?
> would that trigger the rehashing on the Cerberus destination instance as well? I read above and thought "it can't be that easy."
No rehashing would take place here. You would be transferring the hashed password from one Cerberus instance to the other. This is a detail, though, that your code shouldn't need to be aware of.
It looks like converting to JSON and back again is losing information. This error appears when I do this conversion:
Cannot create object of type "CerberusFtp.VirtualDirectory". Cannot convert the "CerberusFtp.DirectoryPermissions" value of type "System.String" to type "CerberusFtp.DirectoryPermissions"
I suspect the JSON conversion is using a non-reversible toString conversion.
I suggest you side-step all of this by avoiding the remote powershell session. The SOAP ports should be open between these two instances of Cerberus, as this is required for the regular Sync operation.
You should be able create two service objects, $LocalCerberusSoapService and $RemoteCerberusSoapSerivice and never have to pass the object between powershell sessions. (I *think* you could also just change $CerberusSOAPService.Url depending on which Cerberus instance your script needs to communicate with).
0 -
Vincent,
I wanted to give an update, I finally figured this out and got it working in testing.
The trick was using the LOCAL WSDL for the remote call to the Remote SOAP URL and I had to get around a PowerShell WebserviceProxy bug that affects types when casting ("[CerberusFTPService.GetUserInformationResponse]") methods to methods that are used in many of the API tutorials. The exception error for this specific bug coming from System.Management.Automation is something along the lines of "Cannot convert type "CerberusFTPService.GetUserInformationResponse" to type CerberusFTPService.GetUserInformationResponse". To get around the bug use $LocalCerberusSOAPServiceType=$LocalCerberusSOAPService.GetType().Namespace after creating the webserviceproxy object. When creating Objects you would normally Cast to, use New-Object ($LocalCerberusSOAPServiceType + '.GetUserInformationRequest') to pass the correct type.
For Reference and others who follow looking for the solution,see below. This has been scrubbed for security, so use the Private IP of your listeners in the VariablesMapping Section. Also note the SOAP IP in the URL is not localhost or loopback (127.0.0.1), again use the Private IP. This script was setup to be part of a Scheduled Task on each server and is designed to be idempotent.
####################################################
#Inputs
####################################################
$AccountUserName="sftp.test5"####################################################
$VerbosePreference="Continue"
$ErrorActionPreference="Stop"
$Error.Clear()
####################################################
$CerberusFTPCredentialfile=$null$Script:CerberusSOAPRequestCredentials=$null
####################################################
#Variables Mapping
####################################################
$Script:ErrorLogPath = "C:\Temp\SyncTest.log"
$CerberusFTPCredentialFile="C:\Temp\CerberusSOAP.cred"
If($Env:COMPUTERNAME -eq "cerb01"){
$LocalServerWSDL="https://yourserverlistenerPrivateIP:8808/wsdl/Cerberus.wsdl"
$LocalServerSOAP="https://yourserverlistenerPrivateIP:10001/service/CerberusSOAPService"
$RemoteServerSOAP="https://yourserverlistenerPrivateIP:10001/service/CerberusSOAPService"
}
If($Env:COMPUTERNAME -eq "cerb02"){
$LocalServerWSDL="https://yourserverlistenerPrivateIP:8808/wsdl/Cerberus.wsdl"
$LocalServerSOAP="https://yourserverlistenerPrivateIP:10001/service/CerberusSOAPService"
$RemoteServerSOAP="https://yourserverlistenerPrivateIP:10001/service/CerberusSOAPService"
}
####################################################
#Local Server ScriptBlock
####################################################
Write-Output "Starting Call to Local SOAP Service"
####################################################
#Prepare TLS v1.2 and Credentials
####################################################
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
####################################################
#Create Web Service Object to SOAP Connection
####################################################
$LocalCerberusSOAPService=New-WebServiceProxy -Uri $LocalServerWSDL -Credential (Import-Clixml -Path $CerberusFTPCredentialFile)
$LocalCerberusSOAPServiceType=$null
$LocalCerberusSOAPServiceType=$LocalCerberusSOAPService.GetType().Namespace
####################################################
If($LocalCerberusSOAPService){
$LocalCerberusSOAPService.Url=$LocalServerSOAP
$LocalCerberusSOAPService.PreAuthenticate = $true;
####################################################
#Create Request Credential Object
####################################################
$Script:CerberusSOAPRequestCredentials = New-Object ($LocalCerberusSOAPServiceType + '.Credentials')
$Script:CerberusSOAPRequestCredentials.user = (Import-Clixml -Path $CerberusFTPCredentialFile).UserName
$Script:CerberusSOAPRequestCredentials.password = (Import-Clixml -Path $CerberusFTPCredentialFile).GetNetworkCredential().Password
####################################################
Write-Output "Connected Successfully with WebserviceProxy to Local SOAP Service"
####################################################
}
Else{
Write-output "Cannot Connect to Local SOAP API"
Return
}
$GetLocalUserObjectRequest=$null
$GetLocalUserObjectRequestResponse=$null
####################################################
#Create Get User Information Request Object
####################################################
#[CerberusSOAPService.GetUserInformationRequest]
$GetLocalUserObjectRequest = New-Object ($LocalCerberusSOAPServiceType + '.GetUserInformationRequest')
$GetLocalUserObjectRequest.credentials = $Script:CerberusSOAPRequestCredentials
$GetLocalUserObjectRequest.userName = $AccountUserName
####################################################
# Create Get User Information Response Object
####################################################
#[CerberusSOAPService.GetUserInformationResponse]
#$GetLocalUserObjectRequestResponse = New-Object ($LocalCerberusSOAPServiceType + '.GetUserInformationResponse')
$GetLocalUserObjectRequestResponse =$LocalCerberusSOAPService.GetUserInformation($GetLocalUserObjectRequest)
####################################################
Write-Output "Local SOAP Service Response"
$GetLocalUserObjectRequestResponse.result
$GetLocalUserObjectRequestResponse.message
$Script:UserObject=$GetLocalUserObjectRequestResponse.UserInformation
########################################################################################################
#Remote Server ScriptBlock
####################################################
Write-Output "Starting Call to Remote SOAP Service"
#Prepare TLS v1.2 and Credentials
####################################################
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
####################################################
#Create Web Service Object to SOAP Connection
####################################################
$RemoteCerberusSOAPService=New-WebServiceProxy -Uri $LocalServerWSDL -Credential (Import-Clixml -Path $CerberusFTPCredentialFile)
$RemoteCerberusSOAPServiceType=$null
$RemoteCerberusSOAPServiceType=$RemoteCerberusSOAPService.GetType().Namespace
####################################################
If($RemoteCerberusSOAPService){
$RemoteCerberusSOAPService.Url=$RemoteServerSOAP
$RemoteCerberusSOAPService.PreAuthenticate = $true;
####################################################
#Create Request Credential Object
####################################################
$Script:CerberusSOAPRequestCredentials = New-Object ($RemoteCerberusSOAPServiceType + '.Credentials')
$Script:CerberusSOAPRequestCredentials.user = (Import-Clixml -Path $CerberusFTPCredentialFile).UserName
$Script:CerberusSOAPRequestCredentials.password = (Import-Clixml -Path $CerberusFTPCredentialFile).GetNetworkCredential().Password
####################################################
Write-Output "Connected Successfully with WebserviceProxy to Remote SOAP Service"
####################################################
}
Else{
Write-output "Cannot Connect to Remote SOAP API"
Return
}$UpdateRemoteUserObjectRequest=$null
$UpdateRemoteUserObjectResponse=$null
####################################################
# Create new $UpdateUserRequest object
####################################################
#[CerberusSOAPService.AddUserRequest]
$UpdateRemoteUserObjectRequest = New-Object ($RemoteCerberusSOAPServiceType +'.AddUserRequest')
$UpdateRemoteUserObjectRequest.credentials = $Script:CerberusSOAPRequestCredentials
####################################################
# Populate request object with Updated user object
####################################################
$UpdateRemoteUserObjectRequest.User = $Script:UserObject
$UpdateRemoteUserObjectRequest.saveToDisk
$UpdateRemoteUserObjectRequest.saveToDiskSpecified = $true
####################################################
# Create new $UpdateUserResponse object
####################################################
#[CerberusSOAPService.AddUserResponse]
$UpdateRemoteUserObjectResponse = $RemoteCerberusSOAPService.AddUser($UpdateRemoteUserObjectRequest)
Write-Output "Remote SOAP Service Response"
Write-Output $UpdateRemoteUserObjectResponse.Result
Write-Output $UpdateRemoteUserObjectResponse.Message
####################################################
#Cleanup Connections
####################################################
Write-Output "Cleaning Up WebserviceProxy Objects"
####################################################
$LocalCerberusSOAPService.Dispose()
$RemoteCerberusSOAPService.Dispose()0 -
Brilliant!
> The trick was using the LOCAL WSDL for the remote call to the Remote SOAP URL
I could see this being required if the your remote Cerberus instance does not have an accessible HTTPS Admin listener. It's not uncommon to restrict the admin port to local connections only.
> and I had to get around a PowerShell WebserviceProxy bug that affects types when casting ... The exception error for this specific bug coming from System.Management.Automation is something along the lines of "Cannot convert type "CerberusFTPService.GetUserInformationResponse" to type CerberusFTPService.GetUserInformationResponse".
I've seen this before, usually when I call New-WebServiceProxy multiple times in the same powershell session. I haven't delved too deeply, but I get this impression:New-WebServiceProxy creates a collection of .NET types to manipulate Cerberus' SOAP interface. Calling New-WebServiceProxy a second time recreates the types. Since both calls populate the same namespace, the types are re-generated.
The script has objects created with the first set of types, then create a new set of types and tries to pass them to the new proxy object. Despite identical WSDL between the first and second calls to New-WebServiceProxy, .NET's type system treats them as unique and will not automatically convert between them.
I get the feeling that a second call to New-WebServiceProxy shouldn't be necessary. It seems like the same instance of $CerberusSvc should be usable for both the remote and local server. I'll have to experiment with this.
Great work. Thank you for contributing the code sample!
0 -
PGrant,
I've taken the liberty of creating a support ticket for you. I can't promise any timeline on the two issues you've identified, but the support ticket will ensure you get notified as progress is made.
* Method of triggering sync from SOAP API
* Fine-grained Sync for User password changes
Thanks!
0
Please sign in to leave a comment.
Comments
8 comments