Issues running Remote powershell after upgrading from 8.0->8.3p3

After we have upgraded our Staging environment from 8.0 to 8.3p3 we are seeing some weird behavior when trying to run powershell scripts through IQService. We created the rule below in order to try to troubleshoot the issue we found after the upgrade. We are able to reproduce the same behavior where IQService receives the Script but doesn’t process it/run it. Have been running IQService in Console mode, and don’t see any messages for it either.

 <Source>
  import sailpoint.api.SailPointContext;
import sailpoint.connector.RPCService;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningPlan.Operation;
import sailpoint.object.RpcRequest;
import sailpoint.object.RpcResponse;
import sailpoint.object.Rule;
import sailpoint.tools.GeneralException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

        // Map to supply to the RPC Service to run PowerShell on IQService
        Map data = new HashMap();

// Get the application definition
        Application connect = context.getObjectByName(Application.class, "Application");
//logger.debug("Fetched connect object");

// Get the Rule definition
        Rule theRule = context.getObjectByName(Rule.class, "The Remote Rule");
//logger.debug("Fetched rule");

// Get the Account Request
// Fake account request
        AccountRequest accountRequest = new AccountRequest();
        accountRequest.setApplication(connect.getName());
        accountRequest.setNativeIdentity("*FAKE*");
        accountRequest.setOperation(AccountRequest.Operation.Modify);

// Fake attribute request
        AttributeRequest fakeAttribute = new AttributeRequest();
        fakeAttribute.setOperation(Operation.Add);
        fakeAttribute.setName("CmdletResponse");
        fakeAttribute.setValue("");
        List fakeAttributeRequests = new ArrayList();
        fakeAttributeRequests.add(fakeAttribute);
        accountRequest.setAttributeRequests(fakeAttributeRequests);


// Add to the IQService params
        data.put("Request", accountRequest);

//logger.debug(String.format("Lockout request: %s", req.toXml()));

// Populate the RPC 'data' argument
Application ad = context.getObjectByName(Application.class, "Application");
data.put("Application", ad.getAttributes());

        data.put("postScript", theRule);
  


// Build the RPC Service
        RPCService service = new RPCService("server", 5050, false);
  service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());
accountRequest.add(new AttributeRequest("username", "Credential"));
String decrypted = "Password";
String encrypted = service.encode(decrypted);
accountRequest.add(new AttributeRequest("password", encrypted));
//logger.debug("Built RPC Service");

// Build the RPC Request
        RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);
//logger.debug("Built RPC Request");

// Execute the request
        RpcResponse response = service.execute(request);
//logger.debug(String.format("RPC Response: %s", response.toXml()));

        return response.toXml();
  
</Source>

Can you also share the Powershell rule "The Remote Rule" and the IQService.exe location ?

In the past I has similar issues when we installed a new/other IQService version. When placed in a different directory the PowerShell rule needed to be updated to find the utils.dll

Taken from a SSB (so using target.properties):

#Refer to SailPoint class library.
Add-type -path %%IQSERVICE_PATH%%\utils.dll

– Remold

<Rule created="" id="" language="beanshell" modified="" name="The Remote Rule">
  <Attributes>
    <Map>
      <entry key="ObjectOrientedScript" value="true"/>
      <entry key="disabled" value="false"/>
      <entry key="extension" value=".ps1"/>
      <entry key="program" value="powershell.exe"/>
      <entry key="timeout" value="100"/>
    </Map>
  </Attributes>
  <Source>
 Add-type -path d:\iqservice\Utils.dll
"Hello World" | Out-File D:\IQService\stuff.txt
  </Source>
</Rule>

What is you enable debug logging of the IQService: IQService.exe -d -l 3

The strangest thing is; the response states it was a success.
Does the user running IQService has write access to D:\IQService\stuff.txt ?

You can also add a return message in the powershell (The Remote Rule). Something like:

#Read the environment variable
$sResult = New-Object System.IO.StringReader([System.String]$env:Result);

#Form the xml reader object
$xmlReader_Result = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sResult));

#Create SailPoint object
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult($xmlReader_Result);

#Add message to resultObject
$resultObject.Messages.add("This is a message from PS to IIQ");

If you place this after the ‘Hello World’ statement, you should see it back in the response.toXml();

I hope this brings you a step furher.

– Remold

Here is the error we are seeing when I pushed the logging up to 3…

07/12/2023 11:23:11 : ADConnectorServices [ Thread-6 ] DEBUG : "ENTER runAfterScript"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "ENTER executePostScript"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "ENTER executeScript"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "User : "
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Disabled : false"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Timeout : 100"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Program : powershell.exe"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Extension : .ps1"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Script : 
 Add-type -path d:\iqservice\Utils.dll;
"Hello World" | Out-File D:\IQService\stuff.txt; 
  "
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Script File : D:\IQService\Script_1660ed27-c1ff-44e3-a5f7-51d224f43e5d.ps1"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Return File : D:\IQService\Script_1660ed27-c1ff-44e3-a5f7-51d224f43e5d.tmp"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Arguments = -file "D:\IQService\Script_1660ed27-c1ff-44e3-a5f7-51d224f43e5d.ps1" "D:\IQService\Script_1660ed27-c1ff-44e3-a5f7-51d224f43e5d.tmp""
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "Exception occurred in executing the script : The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. "
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "EXIT executeScript"
07/12/2023 11:23:11 : AbstractConnector [ Thread-6 ] DEBUG : "EXIT executePostScript"
07/12/2023 11:23:11 : ADConnectorServices [ Thread-6 ] DEBUG : "EXIT runAfterScript"
07/12/2023 11:23:11 : RpcHandler [ Thread-6 ] INFO : "OutgoingResponse:
<?xml version="1.0" encoding="utf-8"?>
<RpcResponse version="1.0" requestId="1660ed27-c1ff-44e3-a5f7-51d224f43e5d" complete="true">
  <ResultAttributes>
    <Map>
      <entry key="requestProcessedOn" value="7/12/2023 11:23:11 AM" />
    </Map>
  </ResultAttributes>
</RpcResponse>"
07/12/2023 11:23:11 : RpcHandler [ Thread-6 ] DEBUG : "ENTER Close"
07/12/2023 11:23:11 : RpcHandler [ Thread-6 ] DEBUG : "EXIT Close"

Wanted to update this thread with some findings…

It appears that our issue is rooted in the Application being passed to the script. We tried with other applications and everything goes through perfectly.

Will try to investigate and try to see what could be the issue. Need to try and figure it out since this is an old “legacy” Exchange application that is still leveraged for old custom code.

1 Like

Hello Remold,

We have experiences issues with getting messages returned back from powershell to IIQ despite adding the return message in powershell as you have listed here.

We have peeled back our powershell code to do the bare minimum - read the environment variable, form the xml reader object, create the result object, and add the message back to the result object. The result back to IIQ is just:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE String PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<String><?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE RpcResponse PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<RpcResponse complete="true" requestId="f03cf176-318c-4c53-b2a4-128380182f80" version="1.0">
  <ResultAttributes>
    <Map>
      <entry key="requestProcessedOn" value="7/24/2023 3:02:07 PM"/>
    </Map>
  </ResultAttributes>
</RpcResponse>
</String>		

Do you have any suggestions? I am not sure if Jose tried what you suggested but I noticed that they do not have any messages sent back in their RcpResponse either. Having these messages sent back to IIQ would be a huge help for us for future debugging efforts.

Thanks,
Eva

Hi Evangelina ,

Can you test the following powershell script? And look to the IQService log file.
This script will add an error and a message to the result and before ending the script send the resultObject to the IQService log"

Add-type -path d:\iqservice\utils.dll

Read the environment variables
$sReader = New-Object System.IO.StringReader([System.String]$env:Request);
$sResult = New-Object System.IO.StringReader([System.String]$env:Result);

#Form the xml reader objects
$xmlReader = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sReader));
$xmlReader_Result = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sResult));

#Create SailPoint objects
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult($xmlReader_Result);

$resultObject.Messages.add("A Message in the Return Object");
$resultObject.toxml() | out-file $args[0];

This should show the resultObject in the IQService log file and also in IIQ.

If really needed I can setup my PoC environment to do exactly the same, but that would take some extra time :face_with_diagonal_mouth:

– Remold

Some extra info:

If you want to use a different log file you can cahneg the last line to:

$resultObject.toxml() | Out-File -FilePath "D:\log.txt" -Append;

if you want to return an error, make sure the ‘RPCservice’ calling the PowerSheel has checkError disabled.

service.checkForErrors(false); // Otherwise execute will throw an error if the response contains error messages
response = service.execute(request);

Adding an error to the resultObject :

$resultObject.Errors.add("Error message to be passed back to IIQ");

An extra note:
It appears only the after scripts return the ResultObject.
The powershell should be called as an after script rule being ConnectorAfterCreate, ConnectorAfterModify or ConnectorAfterDelete.

So in the PowerShell XML something like:

<Rule language="beanshell" name="Test Powershell Script" type="ConnectorAfterCreate">

And when creating the RpcRquest use:

RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);

– Remold

1 Like

This works! However, for our use case we are using Start-Job and having this code run within a job. Due to that we are unable to extract the output back to IIQ. Do you know if there is any way to store information within the resultAttributes as that we can get back to IIQ with no issue even when using jobs.

Thanks for your help!
Eva

1 Like

Hi Eva,

This looks more like a PowerShell question :slight_smile:

Without testing I assume you need the following steps:

  • From IIQ start the Powershell script using the RPCService

    RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);
    RpcResponse response = service.execute(request);
    
  • Within the PowerShell script start the job and wait for the results. This can be performed using:

    $results = start-job { ... } | wait-job | receive-job
    
  • Get the data from the $results variable and place them in $resultObject

    $resultObject.Messages.add($result);
    
  • In IIQ get the (last) message from the response

    List messages = response.getMessages();
    if (!Util.isEmpty(messages)) {
      String message = messages.get(messages.size() - 1);
    }
    

The beanshell variable message now contains the result from the Start-Job.

– Remold

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.