cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Running Powershell directly via the IQService

Running Powershell directly via the IQService

It's sometimes necessary to run a Powershell script "out of band" (i.e. from a Workflow or Run Rule task). This is not well-suited to the Before/After model used by IQService connectors. In this article, I will go through how the IQService invokes Powershell and how you can hook into this process to run your own Powershell scripts.

I've seen a lot of code floating around for calling Powershell, but nothing that explains why you're using that specific code.

Calling the IQService from code

The IQService is a .NET application that listens on a configured port for commands from IIQ. These commands are XML-RPC blobs that are encrypted using TLS and/or an agreed-upon private key. Ordinarily, usage of the IQService is buried within IIQ's connector code. However, since you can use any part of the IdentityIQ API in your custom workflows and rules, the IQService classes are available for you too.

These are:

  • RPCService: The service class that handles all of the communication from IIQ to the IQService, including serialization and encryption of requests and responses.

  • RpcRequest: Wraps a request to the IQService, designating which type of command is being sent and what the arguments to that command are. Different commands will require different arguments, but you'll always be using the ScriptExecutor command.

  • RpcResponse: Wraps the response from the IQService. What gets returned depends heavily on what the command is, as we'll see below.

To make a call to a Powershell script, you will need to:

  • Construct an RpcRequest using a service name of ScriptExecutor, also passing your Powershell code and other arguments.

  • Construct an RPCService instance, passing the connection parameters for your IQService instance.

  • Invoke RPCService.execute() against your RpcRequest.

  • Interpret the RpcResponse returned from execute().

The ScriptExecutor service

The IQService exposes the ScriptExecutor service for running command line utilities. This can run not only Powershell but also Windows bash or other command line tools. The parameters to the command in question are passed as part of your RpcRequest.

There are two types (more or less) of script executors:

  • runBeforeScript: In the usual model, these are intended to run before execution, like a Before Provisioning rule. Consequently, they can modify the passed AccountRequest, but they cannot return any errors, warnings, or other messages.

  • runAfterScript: In the usual model, these are intended to run after execution, like an After Provisioning rule. Consequently, they cannot modify the passed AccountRequest (because it's already been executed). However, they can return errors, warnings, or other messages.

For a runBeforeScript, you must pass a preScript parameter with your Rule. For a runAfterScript, you must pass a postScript parameter with your Rule.

I tend to prefer runAfterScript for ad hoc Powershell, just because returning error messages is useful.

The code

The following is the simplest bit of code that will invoke Powershell on the server. Note the use of postScript and runAfterScript.

import sailpoint.object.RpcRequest;
import sailpoint.object.RpcResponse;
import sailpoint.connector.RPCService;

Map data = new HashMap();
data.put("postScript", yourPowershellRuleObject);
RPCService service = new RPCService(iqServiceHost, iqServicePort, false, useTLS);
RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);
RpcResponse response = service.execute(request);

If you want to pass additional values to your script, you will need use the AccountRequest object. Specifically, create a new AccountRequest and add any parameters you'd like to pass as AttributeRequests. This must be added to your data Map as the field Request. 

This Request field is not optional, even if you don't actually use it. Your RPC call will fail if you don't include it.

// Fake account request
AccountRequest accountRequest = new AccountRequest();
accountRequest.setApplication("IIQ");
accountRequest.setNativeIdentity("*FAKE*");
accountRequest.setOperation(AccountRequest.Operation.Modify);

// Fake attribute request
AttributeRequest fakeAttribute = new AttributeRequest();
fakeAttribute.setOperation(Operation.Add);
fakeAttribute.setName(paramName);
fakeAttribute.setValue(paramValue);
fakeAttributeRequests.add(fakeAttribute);
accountRequest.setAttributeRequests(fakeAttributeRequests);

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

Client authentication

If you are using client authentication, a newer security feature for the IQService available since 2019, you will need to additionally pass an Application object's Attributes with IQService configuration in your parameters.

For example:

Application ad = context.getObjectByName(Application.class, "Customer Active Directory");
data.put("Application", ad.getAttributes());

The RPCService will automatically use the configuration stored in that application, specifically the IQServiceUser and IQServicePassword attributes.

You will also need to provide it with an instance of ConnectorServices, which the RPCService will use to decrypt the passwords stored in the Application object. Failing to do this will result in a NullPointerException.

service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());

The Powershell side

Temporary script file

The IQService will write your entire Rule's source to a temporary file, which by default is in the same directory. These files are intended to be deleted once the script completes, but the IQService is (for some reason) sometimes unable to do that. If you see a number of temp objects piling up in your IQService folder, you can safely delete them.

However, since the IQService writes each script to disk, you should not hardcode any credentials in your Powershell rules.

Receiving the input

Receiving the values passed to Powershell rules (in that fake AccountRequest) is a bit convoluted. To support various types of command line scripts, the IQService passes all parameters as environment variables. To make things more confusing, it passes them in the environment variables as XML.

Reading your AccountRequest input in Powershell thus looks like:

Add-type -path utils.dll
$sReader = New-Object System.IO.StringReader([System.String]$env:Request); 
$xmlReader = [System.xml.XmlTextReader]([sailpoint.Utils.xml.XmlUtil]::getReader($sReader)); 
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult;

In the first line, utils.dll is a utility provided by SailPoint that exposes several useful Powershell object types. We are using some .NET classes to read the data out of $env:Request, which is where the IQService stuffs our AccountRequest object.

Finally, we create a Sailpoint.Utils.objects.ServiceResult, which we'll use later to return the results from our script.

Interpreting the input

To interpret the Powershell input, you will need to read the data out of the AccountRequest. Fortunately, the API is identical to that in IIQ Beanshell. To make things easier, though, I like to build a Powershell hash object (i.e. a Map) with the data.

$attributes = @{}
foreach ($attribute in $requestObject.AttributeRequests){
$attributes[$attribute.Name] = $attribute.Value;
}

After this code, you can simply refer to $attributes["name"] to get at your passed data. Note that printing this value will just produce a hash.

Handling output

Once you've completed your Powershell actions, you will need to return some values back to IIQ. You will do this by dumping an object XML to the filename passed as the first parameter to your script. The IQService will read an appropriate object out of that file and pass it back to IIQ. (As with the input, this indirect method is used so that any number of scripting interfaces, not only Powershell, can be invoked by the IQService.)

I like to wrap the entire thing in a Try/Catch/Finally block so that the output is always written and errors are properly handled.

Try {
# Handle input and do stuff here
$resultObject.Messages.add("Success!");
} catch [Exception] {   
# You should probably do some logging here too
$ErrorMessage = $_.Exception.ToString()
$resultObject.Errors.add($ErrorMessage);
} finally {
$resultObject.toxml() | out-file $args[0];
}  

The "magic" takes place in the finally block, in which the $resultObject that we constructed earlier is exported as XML to the destination file. As with the rule source, this output object is being written to disk, so you should not store any sensitive data in your script output.

You can return Messages and Errors, which are simply Powershell lists of string.

For returning more complex objects, use the Attributes hashtable stored on the ServiceResult object. The keys must be strings, but the values can be a variety of simple object types - strings, numbers, dates, hashtables, lists, and byte arrays. Other objects that are not handled by the XML serializer will be dropped from the response, so make sure you convert your data to a handled type.

$resultObject.Attributes["someVariable"] = $someResultObject;

Using a wrapper script

It's nice to avoid boilerplate code wherever possible. To do this, I usually write a Powershell script that resides locally on the IQService host, e.g. in a D:\IQService\Scripts folder, and invoke those scripts once I've parsed my input. The rules in IIQ are responsible only for ensuring that the correct data is passed to the local scripts.

This also means that those scripts can be reused outside of IIQ, e.g. as part of a batch operation or an administrator action.

Back in IIQ: Interpreting the output

Your $resultObject will be returned to IIQ as an RpcResponse. This will contain any errors, messages, or attributes your Powershell script produced. You may want to create a standard method to check for errors and throw an exception. The following is a barebones error handler.

(Remember that, like most IIQ APIs, pretty much anything can be null at any time.)

public RpcResponse checkRpcFailure(RpcResponse response) throws Exception {
if (response == null) {
return null;
}
if (response.getErrors() != null && response.getErrors().size() > 0) {
throw new IllegalStateException(response.getErrors().toString());
}
return response;
}

 You can use this waaaaay back up in the RPCService code like so:

RpcResponse response = checkRpcFailure(service.execute(request));

If you need to return structured data, passing textual values like JSON or XML as part of a Message is one option, as is returning it as part of the Attributes. See what works best for your purposes.

Comments

@Former_User

If you do not need TLS capabilities, use:

RPCService service = new RPCService(iqServiceHost, iqServicePort);

Hi @drosenbauer ,

Could you please provide an example for wrapper script as mentioned in this article, where you are keeping the powershell script on the IQ service folder locally and passing input variabls/data to the script .

 

Thanks

Hi 

Think i found an issue , after upgrading to 8.1p1  Im getting a strange error and it only shows up in when i set IQService to debug .  

 

"Arguments = -file "E:\IQService\Script_9f06d82f-e142-454e-92d9-668b0a53c75c.ps1" "E:\IQService\Script_9f06d82f-e142-454e-92d9-668b0a53c75c.tmp""
08/19/2020 20:58:19 : AbstractConnector [ Thread-4 ] 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. "

 

when i run the same test using app "nativeRules" trigger it works fine.   here my test code : 


Logger log = LogManager.getLogger("rule.SP.Joiner.RulesLibrary");
log.debug("Enter Identity afterProvisionRule");
Application application = context.getObjectByName(Application.class, "Active Directory");
String identityName = "alfsun97.Test";
// Fake account request
AccountRequest accountRequest = new AccountRequest();
accountRequest.setApplication("IIQ");
accountRequest.setNativeIdentity(identityName);
accountRequest.setOperation(AccountRequest.Operation.Modify);

// Fake attribute request
AttributeRequest attr1 = new AttributeRequest();
AttributeRequest attr2 = new AttributeRequest();
AttributeRequest attr3 = new AttributeRequest();
attr1.setOperation(Operation.Add);
attr1.setName("sAMAccountName");
attr1.setValue(identityName);
attr2.setOperation(Operation.Add);
attr2.setName("UsersAm");
attr2.setValue("admin");
accountRequest.add(attr1);
accountRequest.add(attr2);

Map data = new HashMap();
Rule rule = context.getObjectByName(Rule.class, "AfterCreate-AD Home Drive creation");
data.put("postScript", rule);
data.put("Application", application.getAttributes());
// Add to the IQService params
try{
List iqList = application.getAttributes().get("IQServiceConfiguration");
Map iServiceMap = iqList.get(0);
data.put("Request", accountRequest);
String IQServiceServer = iServiceMap.get("IQServiceHost");
int IQServicePort = Integer.parseInt( iServiceMap.get("IQServicePort"));
log.debug("IQServiceServer - " + IQServiceServer);
log.debug("IQServicePort - " + IQServicePort);
RPCService service = new RPCService(IQServiceServer, IQServicePort, false, false,true);
log.debug("got IQServices");
service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());
RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);
log.debug("build request");
RpcResponse response = service.execute(request);
log.debug("response IQServices: "+ response.toXml());
}catch (Exception e) {
log.error("Exception in afterProvisionRule.." + e.getMessage());
}

log.debug("Exit Identity afterProvisionRule");

-------------------------------------------------------------------------------------------

any ideas what  The input is not a valid Base-64 string is about? 

 

found the issue , my applicaiton.xml had custom Attributes ,which was causing issues. fixed that . working nice now  

 

HI @drosenbauer 

it is very good article and explanation of RPC service. I am trying to use this for account creation/modification using IQService.  It seems running successfully, but I am not seeing account created. Here is my code

Powershell Rule

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="PowershellRuleObject" type="ConnectorAfterCreate">
<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="120"/>
</Map>
</Attributes>
<Description>
This example is for IQService Script.
</Description>
<Signature returnType="Map">
<Inputs>
<Argument name="email">
<Description>
Email address to call powershell script
</Description>
</Argument>
</Inputs>
<Returns>
<Argument name="response">
<Description>
Response of the Powershell script.
</Description>
</Argument>
</Returns>
</Signature>
<Source>

/*Reading your AccountRequest input in Powershell */
Add-type -path Utils.dll;

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

# Read the environment variables
$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);

try {

"Result IS $result $args" | Out-File -filepath homedirresult.txt
[System.Collections.ArrayList]$messagesList = @()

$env:Result = $resultObject.toxml();
$PSCommandPath = $MyInvocation.MyCommand.Path;
$PSCommandPath = $PSCommandPath.replace(".ps1",".tmp");
$resultObject.toxml();
$resultObject.toxml() | Out-File -FilePath $PSCommandPath -Append;
} catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$resultObject.Errors.Add($ErrorMessage)
Write-Host $ErrorMessage
Write-Host $args[0]
Write-Error $_ -ErrorAction Continue
#$env:Result = $StringWriter.toString()
#$requestObject.toxml()|out-file $args[0];
}
finally
{

$requestObject.toxml()|out-file -filepath homeRequestObject.txt;
$resultObject.toxml()|out-file -filepath homeResultObject.txt;

}
$outFile = "beforeCreateLog.txt"
Out-File $outFile -InputObject "Test Message" -Append


</Source>
</Rule>

Rule for RPC request

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" modified="1604264137822" name="Rule - IQService">
<Source>

import sailpoint.api.SailPointFactory;
import sailpoint.object.Identity;
import sailpoint.api.SailPointContext;
import sailpoint.tools.GeneralException;
import sailpoint.object.RpcRequest;
import sailpoint.object.RpcResponse;
import sailpoint.connector.RPCService;
import sailpoint.object.Rule;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan.AccountRequest;


List processed = new ArrayList();
List first = new ArrayList();
final Log log = LogFactory.getLog("com.pge.iiq.rule.AAA - Rule-IQService");

Map data = new HashMap();
List attributeRequests = new ArrayList();
// Map mapResp onse = new HashMap();

Rule rule = context.getObjectByName(Rule.class, "PowershellRuleObject");
data.put("postScript", rule);


// Fake account request
AccountRequest accountRequest = new AccountRequest();
accountRequest.setApplication("Active Directory");
accountRequest.setNativeIdentity("CN=E09999,OU=Dev Users,DC=ADM,DC=dom");
accountRequest.setOperation(AccountRequest.Operation.Create);

// Fake attribute request
AttributeRequest attrRequest = new AttributeRequest();
attrRequest.setOperation(Operation.Add);
attrRequest.setName("memberOf");
attrRequest.setValue("CN=AD_Dev_Group_01,OU=Dev Groups,,DC=ADM,DC=dom");
attributeRequests.add(attrRequest);

AttributeRequest attrRequest = new AttributeRequest();
attrRequest.setOperation(Operation.Set);
attrRequest.setName("sAMAccountName");
attrRequest.setValue("E09999");
attributeRequests.add(attrRequest);

AttributeRequest attrRequest = new AttributeRequest();
attrRequest.setOperation(Operation.Set);
attrRequest.setName("userPrincipalName");
attrRequest.setValue("E09999@adm.dom");
attributeRequests.add(attrRequest);

AttributeRequest attrRequest = new AttributeRequest();
attrRequest.setOperation(Operation.Set);
attrRequest.setName("distinguishedName");
attrRequest.setValue("CN=E09999,OU=Dev Users,DC=ADM,DC=dom");
attributeRequests.add(attrRequest);

accountRequest.setAttributeRequests(attributeRequests);


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


Application application = context.getObjectByName(Application.class, "Active Directory");
data.put("Application", application.getAttributes());

String IQServiceServer = "xxxxxxxxxx";
int IQServicePort = xxxx;

RPCService service = new RPCService(IQServiceServer, IQServicePort, false, false);
service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());

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

//RpcResponse response = checkRpcFailure(service.execute(request));
RpcResponse response = service.execute(request);

log.trace("Mamta response XML === "+response.toXml());

mapResponse = response.getResultAttributes();

log.trace("mapResponse.toString() "+mapResponse.toString());

if(response.getErrors() != null)
{
log.trace("inside error response"+response.getErrors());

}
return mapResponse;

</Source>

</Rule>

 

Thanks,

Mamta

 

Hello Everyone,

We are running the PowerShell script via IQservice to  get the hostname's from AD. But  getting the below error.

“Error at executing the  powershellsailpoint.tools.xml.ConfigurationException: No serializer registered for class class bsh.ClassIdentifier”.

--------------------------------------------------------------------------------------------------------------

below is the rule that we are using, 

import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.Operation;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.spring.SpringStarter; 
import sailpoint.tools.GeneralException;
import sailpoint.object.RpcRequest;
import sailpoint.object.RpcResponse;
import sailpoint.connector.RPCService;

String appName="Test Active Directory";
AccountRequest accountRequest = new AccountRequest();
accountRequest.setApplication("IIQ");
accountRequest.setNativeIdentity("*FAKE*");
accountRequest.setOperation(AccountRequest.Operation.Modify);

String paramName = "sAMAccountName";
Object paramValue = "xxxxxxxxx";//Value for test 
AttributeRequest attrReq = new AttributeRequest();
attrReq.setOperation(sailpoint.object.ProvisioningPlan.Operation.Add);
attrReq.setName(paramName);
attrReq.setValue(paramValue);
List attrReqList = new ArrayList();
attrReqList.add(attrReq);
accountRequest.setAttributeRequests(attrReqList);

Rule powershellObj=context.getObjectByName(Rule.class,"Rule_PowerShell_Validate_ADComputer");
Map data = new HashMap();
data.put("postScript", powershellObj);
data.put("Request", AccountRequest);

Application application = context.getObjectByName(Application.class,appName);
if (null != application){
data.put("Application",application.getAttributes());
}else{
throw new GeneralException("could not fetch AD application from context");
}

String iqServiceHost="xxxxxxxx";
RPCService service = new RPCService(iqServiceHost, xxxx, false,false); 
service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());
RpcRequest request = new RpcRequest("ScriptExecutor", "runAfterScript", data);
RpcResponse response=null;
System.out.println("Going to call RPCServices");


try {
response = service.execute(request);
System.out.println("AFTER executing RPCCall****"+response);
} catch (Exception e){
System.out.println("Error at executing the powershell"+e.toString()); 
}

System.out.println("response********"+response); 

return response;

Power shell script 

 <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="350"/>
    </Map>
  </Attributes>
----------------------------------------------------------------------
 <Source>
 Add-type -path "D:\Sailpoint\IQService\Utils.dll";

# Read the environment variables
$sReader = New-Object System.IO.StringReader([System.String]$env:Request);
   
$xmlReader = [System.xml.XmlTextReader]([sailpoint.Utils.xml.XmlUtil]::getReader($sReader)); 
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult;
				 
	$TestLogFile = "D:\Sailpoint\logs\log.txt";

	add-content $TestLogFile "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";
  
	$logDate = Get-Date -Format g

	$computer = "";
	foreach ($attribute in $requestObject.AttributeRequests){
	$attrName = $attribute.Name;
	$attrValue = $attribute.Value;

	if ($attrName -eq "sAMAccountName") {
	  $computer = $attrValue;
	}
	}
	add-content $TestLogFile "Variables passed";
	add-content $TestLogFile "sAMAccountName: " $computer;
	add-content $TestLogFile "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";
	add-content $TestLogFile $logDate;
	add-content $TestLogFile "Starting Script:";
	add-content $TestLogFile "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";
	 
	try{
		$checkname = @(Get-ADComputer $computer)
		if($checkname.Count -eq 1){
			# computer found
			add-content $TestLogFile "The computer is already in AD."
			$resultObject.Messages.add("Success!");
		}
	}
	catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]{
		# computer not found
		add-content $TestLogFile "The computer is not in AD."    
		$resultObject.Messages.add("Failed!");
	}finally {
	  add-content $TestLogFile "Result Object Value";
	  add-content $TestLogFile $resultObject.toxml();
	}  
	add-content $TestLogFile "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";  
	add-content $TestLogFile "Stop Script:";

  
  </Source>

Hi,

I am getting error with import sailpoint.connector.RPCService. IIQ is not able to find this class. I am using IIQ 8.1

 

Regards,

 

 

Hi,

 

Modify this line 

data.put("Request", AccountRequest);

to 

data.put("Request", accountRequest);

 

 

@drosenbauer  @gaurav_jain

Can you please help me on the below error :

<RpcResponse complete="false" requestId="" version="1.0">
<RpcErrors>
<List>
<String>Object reference not set to an instance of an object.</String>
</List>
</RpcErrors>
</RpcResponse>

here is my rule:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule  language="beanshell"  name="Test - Powershell">
  <Source>

  import sailpoint.object.RpcRequest;
  import sailpoint.object.RpcResponse;
  import sailpoint.connector.RPCService;
  import sailpoint.object.Rule;
  import sailpoint.object.*;
  import sailpoint.object.ProvisioningPlan.AccountRequest;
  import sailpoint.object.ProvisioningPlan.AttributeRequest;
  import sailpoint.object.ProvisioningPlan.Operation;
  

  Map data = new HashMap();

 

  Rule rule = context.getObjectByName(Rule.class, "Active Directory - LastLogin");
  data.put("postScript", rule);
  
    // Fake account request
  AccountRequest accountRequest = new AccountRequest();
  accountRequest.setApplication("IIQ");
  accountRequest.setNativeIdentity("*FAKE*");
  accountRequest.setOperation(AccountRequest.Operation.Modify);



  // Fake attribute request
  AttributeRequest fakeAttribute = new AttributeRequest();
  fakeAttribute.setOperation(Operation.Add);
  fakeAttribute.setName("param");
  fakeAttribute.setValue("value");
  accountRequest.add(fakeAttribute);



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

  String IQServiceServer = "serverHost";
  int IQServicePort = 6060;

  RPCService service = new RPCService(IQServiceServer, IQServicePort, false, false);
  service.setConnectorServices(new sailpoint.connector.DefaultConnectorServices());
  service.checkForErrors(false);

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

  RpcResponse response = service.execute(request);
  Map map = response.getResultAttributes();
  return response;

  </Source>
</Rule>

 

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule  language="beanshell"  name="Active Directory - LastLogin" type="ConnectorAfterCreate">
  <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="300"/>
    </Map>
  </Attributes>
  <Source>
  
  $[CmdletBinding()]


$VerbosePreference = "continue"
$user = get-aduser -LDAPFilter "(|(userId=test)(id=test22))" -server example:3268 -ErrorAction Stop
if (!$user) {
    throw "Couldn't find user: $id"
}

if ($user -is [object[]]) {
    throw "More than one user found for $i"
}

($prefix, $domain) = ($user.distinguishedname -split ',dc=')
$domain = $domain -join '.'
$dcs = (get-addomain -Identity $domain).replicadirectoryservers

$maxtime = [datetime]::MinValue
foreach ($dc in $dcs) {
    Write-Verbose $dc
    $d = (Get-ADUser $user.distinguishedname  -server $dc -Properties lastlogondate).lastlogondate
    if ($d -gt $maxtime) {
        $maxtime = $d
    }
}
write-output $maxtime

</Source>
</Rule>

 

IIQ version: 8.1p2

 

Thanks!

Abhishek

 

Put attributes as well in data map - data.put("Application", application.getAttributes());

I've noticed in IIQ 8.1p2 that if I throw an exception in powershell inside a tryblock, and write that back in the catch block using:

 

 

$resultObject.errors.add ("my error message")

 

 

I can't access the RPCResponse object from IIQ at all because the call to (RPCService) service.execute throws an exception before it can be processed by the wrapper function, as in checkRPCFailure(service.execute(<Request>)). As a workaround, I'm now writing errors as messages, i.e.:

 

 

$resultObject.Messages.add("ERROR: my error message")

 

 

and looking at the last Message in order to be able to detect and process errors. This may be the result of an implementation change from the prior instance method RPCService.execute(RPCRequest), because if I log out the resultObject, I get two child elements inside <ServiceResult>: <Errors> and <Messages>, both of which are lists of strings. So, IIQ must be tossing away the Messages. Hope that saves someone else some time

Version history
Revision #:
13 of 13
Last update:
‎Jan 09, 2020 02:26 PM
Updated by:
 
Contributors