Powershell synchronization connector calling GET function instead of Create(Post)

I'm working on a Powershell Synchronization connector and when I run a test the 'GET' function is getting called when I'm trying to call my Create(Post) function. The endpoint is receiving a call from me but, again, it is the GET call and not the POST I'm expecting. I believe the issue is in my USER schema but I've been looking at this thing to too long and am probably overlooking something. I have setup the INSERT operation in the Single object operations section and it does fire my insert process. The job does run successfully in the job queue.

<Properties>
		<Property Name="id" DataType="String" >
			<ReturnBindings>
				<Bind CommandResultOf="Get-AllUsers" Path="id" />
				<Bind CommandResultOf="Create-NewUser" Path="federationIdentifier" />
			</ReturnBindings>	
			<CommandMappings>
				<Map ToCommand="Get-SingleUser" Parameter="id"/>
				<Map ToCommand="Update-ExistingUser" Parameter="username"/>
			</CommandMappings>
		</Property>
		<Property Name="federationIdentifier" DataType="String" IsUniqueKey="true" IsDisplay="true" IsMandatory="true">
			<ReturnBindings>
				<Bind CommandResultOf="Get-AllUsers" Path="federationIdentifier" />
				<Bind CommandResultOf="Create-NewUser" Path="federationIdentifier" />
			</ReturnBindings>  
			<CommandMappings>
				<Map ToCommand="Get-SingleUser" Parameter="ID"/>
				<Map ToCommand="Update-ExistingUser" Parameter="ID"/>
			</CommandMappings>
		</Property>
		<Property Name="username" DataType="String"  IsMandatory="true">
			<ReturnBindings>
				<Bind CommandResultOf="Get-SingleUser" Path="username"/>
				<Bind CommandResultOf="Create-NewUser" Path="username" />
			</ReturnBindings>
			<ModifiedBy>
				<ModBy Command="Update-ExistingUser" />
			</ModifiedBy>	
			<CommandMappings>
				<Map ToCommand="Create-NewUser" Parameter="username"/>
				<Map ToCommand="Update-ExistingUser" Parameter="username"/>
			</CommandMappings>
		</Property>
	
		<!--name-->
		<Property Name="name" DataType="String" >
			<ReturnBindings>
				<Bind CommandResultOf="Get-SingleUser" Path="name"/>
				<Bind CommandResultOf="Create-NewUser" Path="name" />
			</ReturnBindings>
			<ModifiedBy>
				<ModBy Command="Update-ExistingUser" />
			</ModifiedBy>	
			<CommandMappings>
				<Map ToCommand="Create-NewUser" Parameter="name"/>
				<Map ToCommand="Update-ExistingUser" Parameter="name"/>
			</CommandMappings>
		</Property>
		<!--Removed addtional properties so save on space but they were essentially set up the same as above-->
	</Properties>

<ReadConfiguration>
		<ListingCommand Command="Get-AllUsers">
				<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
				<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
				<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
				<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
				<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
		</ListingCommand>
		<CommandSequence>
			<Item Command="Get-SingleUser" Order="1" >
				<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
				<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
				<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
				<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
				<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
			</Item>
		</CommandSequence>
	</ReadConfiguration>
	<MethodConfiguration>
		<Method Name="Insert">
			<CommandSequence>
				<Item Command="Create-NewUser" Order="1" >
					<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
					<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
					<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
					<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
					<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
				</Item>
			</CommandSequence>
		</Method>
		<Method Name="Update">
			<CommandSequence>
				<Item Command="Update-ExistingUser" Order="1" >
					<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
					<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
					<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
					<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
					<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
				</Item>
			</CommandSequence>
		</Method>
	</MethodConfiguration>

Parents Reply Children
  • Yeah,  can you please provide the function code for Create-NewUser?

  • <CustomCommand Name="Create-NewUser">
    	<![CDATA[
    		param(
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$BaseURL,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$Username,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$Password,
    			[parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$applicationName,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$applicationEnvironment,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$name,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$lastName,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$firstName,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$companyName,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$division,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$department,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$title,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$street,
    			[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    			[ValidateNotNullOrEmpty()]
    			[String]$email,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$isActive,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$profileId,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$federationIdentifier,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$otherCounty,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$otherCounty2,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$otherCounty3,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$ownerCounty,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$accessRequestJustification,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$phone,
    			[parameter(ValueFromPipelineByPropertyName=$true)]
    			[String]$mobilePhone
    		)
    		#Start Create  user
    		$uri = "$BaseURL/users"
    		$body=[ordered]@{
    			name=$name
    			lastName=$lastName
    			firstName=$firstName
    			companyName=$companyName
    			division=$division
    			department=$department
    			title=$title
    			street=$street
    			country="USA"
    			email=$email
    			isActive=$isActive
    			profileId=$profileId
    			federationIdentifier=$federationIdentifier
    			otherCounty=$otherCounty
    			otherCounty2=$otherCounty2
    			otherCounty3=$otherCounty3
    			ownerCounty=$ownerCounty
    			accessRequestJustification=$accessRequestJustification
    			phone=$phone
    			mobilePhone=$mobilePhone
    			permissionSetId=$permissionSetId
    		}
    		$headers = @{
    			client_id = $Username
    			client_secret = $Password
    			appEnv = $applicationEnvironment
    			appName = $applicationName
    		}
    		$bodyJSON = $body | ConvertTo-Json -Compress
    		$contentType = "application/json"
    		$call=Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $bodyJSON -ContentType $contentType
    		$callJSON=$call | ConvertTo-Json -Compress
    		return $callJSON
    	]]>
    </CustomCommand>

  • Okay, so a few things...

    Behaviour:

    1. "INSERT" event sent to sync for user
    2. Read forward (target) performed for all users
    3. Delta determines if INSERT or UPDATE is called to target

    So, you should expect to see the Get-AllUsers before the Create-NewUser.

    Your script code generates a full body with 21 attributes, but you've only listed 4 above, so I suspect there is an issue with this code somewhere.

    I have a few recommendations that I do to assist with tracing and identifying issues (trimmed example below).

    • Move the PowerShell commands to a ps1 file (uploaded to SoftwareLoader as a base file and tagged for the Job Server and Admin tools). The Connect/Disconnect still has to remain in the Connector Definition, but you can have manual alternatives in the PS1 so you can run the PS1 manually and see expected behaviour.
    • The Sync Project would then have Target Variable for the Script (i.e. ".\sync-script.ps1" - this says "run from current directory (aka Job Server)").
    • Add NLog to the ps1 (tracing is soooooo beneficial).
    • Wrap some try/catches around call outs so you can properly handle errors.
    • I would separate out your Attribute updates too into individual functions. This allows the params to take care of "required", etc which is hard if you have to "optional" all the attributes for a generic update function.

    Example Connector Def and PS1 (7 day limit for this link - tiny booboo in sync-script.ps1 as the Import-Logger function should be called at the global level after it is declared - $global:Log = Import-Logger;)

    Then you can also run manually in ISE as well as trace via NLog:

    cd {jobserverdirectory};
    Import-Module .\sync-script.ps1;
    Connect-ManualTargetSync -BaseURL 'https://some.site/REST/' -UserName 'XXXX' -Password 'ZZZZ';
    Get-AllUsers -BaseURL 'https://some.site/REST/' -UserName 'XXXX' -Password 'ZZZZ' -applicationName 'AppName' -applicationEnvironment 'AppEnv';

  • Here's my updated user class schema. I had previously cut out some additional fields because the "code" block wouldn't allow me to post over a certain amount of lines. I have tested the Powershell code in the IDE and it does make the calls as expected. I am able to use the browse function to look at all of the users and get the individual user information through the sync editor. After making some updates to the schema I do believe that the "Create" function is being called now but I am getting an error: "No Property name found" and the job Freezes. So I guess my next question is why would name not be found. If I remove "name" from my mappings it moves to the next field "companyName" and says it can't find that field. 

    This is a section of my schema to represent all of my fields as I can't seem to post the entire section.

    <!--name-->
    <Property Name="name" DataType="String" >
        <ReturnBindings>
            <Bind CommandResultOf="Create-NewUser" Path="name" />
            <Bind CommandResultOf="Get-AllUsers" Path="name" />
    		<Bind CommandResultOf="Get-SingleUser" Path="name" />
        </ReturnBindings>
    	<ModifiedBy>
    		<ModBy Command="Update-ExistingUser" />
    	</ModifiedBy>	
    	<CommandMappings>
    		<Map ToCommand="Create-NewUser" Parameter="name"/>
    		<Map ToCommand="Update-ExistingUser" Parameter="name"/>
    	</CommandMappings>
    </Property>
    
    <!--firstName-->
    <Property Name="firstName" DataType="String" IsMandatory="true" >
        <ReturnBindings>
            <Bind CommandResultOf="Create-NewUser" Path="firstName" />
            <Bind CommandResultOf="Get-AllUsers" Path="firstName" />
    		<Bind CommandResultOf="Get-SingleUser" Path="firstName" />
        </ReturnBindings>
    	<ModifiedBy>
    		<ModBy Command="Update-ExistingUser" />
    	</ModifiedBy>	
    	<CommandMappings>
    		<Map ToCommand="Create-NewUser" Parameter="firstName"/>
    		<Map ToCommand="Update-ExistingUser" Parameter="firstName"/>
    	</CommandMappings>
    </Property>
    
    <!--lastName-->
    <Property Name="lastName" DataType="String" IsMandatory="true" >
        <ReturnBindings>
            <Bind CommandResultOf="Create-NewUser" Path="lastName" />
            <Bind CommandResultOf="Get-AllUsers" Path="lastName" />
    		<Bind CommandResultOf="Get-SingleUser" Path="lastName" />
        </ReturnBindings>
    	<ModifiedBy>
    		<ModBy Command="Update-ExistingUser" />
    	</ModifiedBy>	
    	<CommandMappings>
    		<Map ToCommand="Create-NewUser" Parameter="lastName"/>
    		<Map ToCommand="Update-ExistingUser" Parameter="lastName"/>
    	</CommandMappings>
    </Property>

    Read and Method Configuration

    			<!--
    				The read configuration is mandatory. It defines the ListingCommand which is used to pull
    				a list of all objects of this type from the system. Furthermore, the additional commandsequence
    				defines the list of commands, that need to be executed to fully load a single object with all
    				properties defined in the schema
    			-->
    			<ReadConfiguration>
    				<ListingCommand Command="Get-AllUsers">
    						<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
    						<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
    						<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
    						<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
    						<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
    				</ListingCommand>
    				<CommandSequence>
    					<Item Command="Get-SingleUser" Order="1" >
    						<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
    						<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
    						<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
    						<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
    						<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
    					</Item>
    				</CommandSequence>
    			</ReadConfiguration>
    
    			<!--
    				The MethodConfiguration section describes all methods that are available for this class. In order to stay compatible with
    				the OneIM Projector, you schould use the name
    					- Insert -> Commands that create a new object
    					- Update -> Commands that modify an exisiting object
    					- Delete -> Commands that delete an exisiting object
    			-->	  
    			<MethodConfiguration>
    				<!--Define a method named "Insert"-->
    				<Method Name="Insert">
    					<CommandSequence>
    						<Item Command="Create-NewUser" Order="1" >
    							<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
    							<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
    							<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
    							<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
    							<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
    						</Item>
    					</CommandSequence>
    				</Method>
    				<Method Name="Update">
    					<CommandSequence>
    						<Item Command="Update-ExistingUser" Order="1" >
    							<SetParameter Param="BaseURL" Source="ConnectionParameter" Value="BaseURL" />
    							<SetParameter Param="Username" Source="ConnectionParameter" Value="Username" />
    							<SetParameter Param="Password" Source="ConnectionParameter" Value="Password" />
    							<SetParameter Param="applicationName" Source="ConnectionParameter" Value="applicationName" />
    							<SetParameter Param="applicationEnvironment" Source="ConnectionParameter" Value="applicationEnvironment" />
    						</Item>
    					</CommandSequence>
    				</Method>
    				
    			</MethodConfiguration>

  • I have seen that before, but in my case it was due to the need for an UPDATE event/call (didn't have to actually do anything), attached to the attribute in the definition, before the attr went out on INSERT. I think there was some Sync Mapping stuff I was fiddling with too (which you haven't provided yet). I assume you're also correctly doing an Update Schema when you alter the definition...

    Here is the complete definition, script, and table relationships for a CyberArk integration I did (7 day limit again). You can use it as a basis for trying to diagnose.

  • Anonymous
    0 Anonymous in reply to Ben

    Thanks for all the help, the problem ended up being missing configuration in the Workflows section.