IdentityNow Rule Guide - After Creation Rule

IdentityNow Rule Guide - After Creation Rule

Purpose

This rule is used to execute PowerShell commands on the IQService component after a source account is created.

 

Execution

  • Connector Execution - This rule executes within the Virtual Appliance and may offer special abilities to perform connector-related functions, and may offer managed connections to sources.
  • Logging - Logging statements are viewable within the ccg.log on the Virtual Appliance and by SailPoint personnel.

 

393243_image.png

 

Input

Argument Type Purpose
Application
System.Collections.Hashtable
Map of the application configuration.
Request
SailPoint.Utils.objects.AccountRequest
A reference to the account request provisioning instructions.
Result
SailPoint.Utils.objects.ServiceResult
A reference to the provisioning result that can be manipulated if needed.

 

Architecture Best Practices

For supportability, it is recommended that the After Creation rule be written with only the most basic logic necessary to trigger a PowerShell script, and shift the bulk of the downstream events and/or modifications to the PowerShell script itself. This script would reside on the client's servers and could therefore be easily maintained or modified by the client as needed. It also allows the client to implement changes to the PowerShell scripted functionality without requiring code review by SailPoint, as the code runs outside of the IdentityNow platform.

 

Rule Template

 

 

 

 

 

 

 

 

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
	<Rule language="beanshell" name="SampleSource AfterCreate" 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>
		<Description>
			This is an IQService afterScript - On a successful provisioning event, this after script should be used as the starting point for 
            initiating a separate PowerShell script residing on the client's IQService server.

            Configuration tasks include:
             - Set a proper location for the $logFile variable
             - Set the proper script location and name for the $command variable
             - Set the $enableDebug flag to $true or $false to toggle debug mode
		</Description>
		<Source>
<![CDATA[

$logDate = Get-Date -UFormat "%Y%m%d"
$logFile = "c:\SailPoint\Scripts\Logs\ConnectorAfterCreate_$logDate.log"
$command = "c:\SailPoint\Scripts\SampleSource-AfterCreate.ps1"
$enableDebug = $false

#====================-------Helper functions-------====================
function LogToFile([String] $info) {
    $info | Out-File $logFile -Append
}

#====================-------Get the request object-------====================
Try{
    if($enableDebug) {
        LogToFile("Entering SailPoint rule")
    }
	
    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);
    $requestAsString = $env:Request

    if($enableDebug) {
        LogToFile("Request as XML object is: $requestAsString")
    }

    #Call the client script
    $command = -join ($command, " -requestString '$requestAsString'")
    Invoke-Expression $command

}Catch{
	$ErrorMessage = $_.Exception.Message 
  	$ErrorItem = $_.Exception.ItemName
  	LogToFile("Error: Item = $ErrorItem -> Message = $ErrorMessage")
}

if($enableDebug) {
    LogToFile("Exiting SailPoint rule")
}

]]>
		</Source>
	</Rule>

 

 

 

 

 

 

 

 

 

 

 

PowerShell Script Template

 

 

 

 

 

 

 

 

###############################################################################################################################
# SETUP
# Instructions (for each IQService host that could run the script):
#   - Update the path to Utils.dll (can be an unqualified path like "Utils.dll" since script is copied to IQService folder for execution)
#   - Make sure Utils.dll is in the specified folder on each IQService host
#   - Be sure the account that runs IQService has appropriate permissions to create directories and set permissions on them
#   - Be sure to set the "run as" account for the IQService in Windows Service to the above-specified account instead of just the "logged on" user
#   - Set a proper location for the $logFile variable
#   - Set the $enableDebug flag to $true or $false to toggle debug mode
###############################################################################################################################

param (
	[Parameter(Mandatory=$true)][System.String]$requestString
)

#include SailPoint library
Add-Type -Path "c:\SailPoint\IQService\Utils.dll";

#import AD cmdlets
Import-Module activeDirectory

#log file info
$logDate = Get-Date -UFormat "%Y%m%d"
$logFile = "c:\SailPoint\Scripts\Logs\SampleSourceAfterCreateScript_$logDate.log"
$enableDebug = $false

###############################################################################################################################
# HELPER FUNCTIONS
###############################################################################################################################

#save logging files to a separate txt file
function LogToFile([String] $info) {
    $info | Out-File $logFile -Append
}

#if we have a non-null account request, get our value; otherwise return nothing
function Get-AttributeValueFromAccountRequest([sailpoint.Utils.objects.AccountRequest] $request, [String] $targetAttribute) {
    $value = $null;
    
    if ($request) {
        foreach ($attrib in $request.AttributeRequests) {
            if ($attrib.Name -eq $targetAttribute) {
                $value = $attrib.Value;
                break;
            }
        }
    } else {
        LogToFile("Account request object was null");
    }
    return $value;
}

  
###############################################################################################################################
# BODY
###############################################################################################################################
if($enableDebug) {
    LogToFile("Entering afterScript")
}

try {

    ##########################
    # Begin SailPoint protected code -- do not modify this code block
    #
        $sReader = New-Object System.IO.StringReader([System.String]$requestString);
        $xmlReader = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sReader));
        $requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
        
        #debug line for testing
        if($enableDebug) {
            LogToFile("Request object contents:")
            LogToFile($requestObject | Out-String)
        }
    #
    # End SailPoint protected code
    ##########################


    ##########################
    # Begin Client-provided code

    #get the necessary info we need from the accountRequest object
    #as an example: $nativeIdentity = $requestObject.nativeIdentity

    #do whatever work needs to be done here

    #
    # End Client-provided code
}
catch {
    $ErrorMessage = $_.Exception.Message 
  	$ErrorItem = $_.Exception.ItemName
  	LogToFile("Error: Item = $ErrorItem -> Message = $ErrorMessage")
}

if($enableDebug) {
    LogToFile("Exiting afterScript")
}

 

 

 

 

 

 

 

 

 

 

 

Comments

Is there documentation for the methods/functions we call from the

Sailpoint.Utils.objects.AccountRequest

 object?

There is one example provided, and one helper function in the above template but other than that the object structure is quite ambiguous.

Hello just an FYI for others I believe the 2nd line in the rule template should be this:

 <!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">

instead of this:

<!DOCTYPE sailpoint PUBLIC "sailpoint.dtd" "sailpoint.dtd">

  

 

In the afterModify script, the following line is not commented out. Is the same true for this one or not?

#Add-type -path utils.dll;

Is there any document available to specify the process of updating rule related details to a Source ?

 

Hi @Sri_Veera , for detail rule review process and guidelines, refer IdentityNow Rule Guide.

Hi @dinesh_mishra ,

Thanks for your response. But i am looking for json keys and value template for attaching the different rules to source.

eg: "beforeProvisioningRule": "rulename"

 

 

I think it would beneficial to state here that these rule names must be added via "nativeRules" to the AD connector in case ES doesn't attach the rule for you after peer review.

example:

 
[
{
"op": "add",
"path": "/connectorAttributes/nativeRules",
"value": ["Rule AD ConnectorAfterModify"]
}
]

Anyone know what to do with this Rule template? Does it get loaded into the system via API calls?

I'm looking for info on how to load afterCreate rule that runs a PowerShell script.

I found somewhere on the rule guide where it gives the below code as rule object model. 

What do i do with these - the rule template and the rule object model?

{
    "id": "2c91808674a2816a0174af21a6450009",
    "name": "Example WebServices Rule",
    "description": "This is just an example",
    "created": "2020-09-21T05:27:32.170Z",
    "modified": null,
    "type": "WebServiceBeforeOperationRule",
    "signature": {
        "input": [],
        "output": null
    },
    "attributes": {
        "sourceVersion": "2020-09-21 05:27:31"
    },    
    "sourceCode": {
        "version": "2020-09-21 05:27:31",
        "script": "\n      import java.util.HashMap;\n      import org.json.JSONArray;\n      import org.json.JSONException;\n      import org.json.JSONObject;\n\n      import org.apache.http.HttpEntity;\n      import org.apache.http.HttpResponse;\n      import org.apache.http.client.HttpClient;\n      import org.apache.http.client.methods.HttpPost;\n      import ...
requestEndPoint.getBody().put(\"jsonBody\",requestXML); \n              }\n        }\n    }\n    log.info(\"Done Ultipro Onboarding before operation rule...\");\n    return requestEndPoint;\n"
    }
}

 

Hello @Ola5 ,

The Json you get (more a sample than a template) is for another event (see type WebServiceBeforeOperationRule).

The Json is for IdentityNow API, don't if it's the same for IIQ.

You need to create a Json for your specific Rule with the good type :
ConnectorAfterCreate
* WebServiceAfterOperationRule

Depends what you want to do. In the sourceCode/script put your script.

See IdN API for the Rule creation https://developer.sailpoint.com/apis/beta/#operation/createConnectorRule.

Best regards,

Hi @Ola5 ,

Were you able to resolve your query? I have also the similar use case i.e. to write after create rule and calling a PS from it. But i dont understand how to prepare JSON for this.

{
"name": "ActiveDirectoryAfterCreateRule",
"type": "ConnectorAfterCreate",
"sourceCode": {
"version": {},
"script": {
"value": "<----My code-->"
}
},
"description": "This rule does that",
"signature": {
"input": [],
"output": null
},
"attributes": {}
}

In the "My code" section how to include powershell code to call the script i am not able to understand.

 

Thanks

 

 

Where do i put the entry keys on the JSON body?

@yunus_ali yes, i was able to resolve it. The value for "scripts" in your json body for your connector rule would take your powershell code converted to json. 

Sample:

{
"description": "This rule does this and that",
"type": "RuleType",
"signature": {
"input": [],
"output": null
},
"sourceCode": {
"version": "1.0",
"script": "#Entering Powershell Code\r\n$service = get-service\r\nWrite-Output \"All services:\" $service"
},
"attributes": {
"ObjectOrientedScript": "true",
"extension": ".ps1",
"sourceVersion": "1.0",
"disabled": "false",
"program": "powershell.exe",
"timeout": "360"
},
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "Rule Name"
}

2021-11-29 12_11_32-Windows PowerShell ISE.png

 

@Ola5 Thanks for your response.

Could you please tell me how did you tag this rule to your active directory source? What exact api and key you have used to tag it?

I tried with "nativeRules" and "connectorAfterCreate" but it doesnt seem to invoke the rule. 

 

Thanks

Yunus

 

Hi @yunus_ali 

You have to use the PATCH method on "https://tenant.api.identitynow.com/beta/sources/{sourceID}"

In the header, you have to specify Content-Type as "application/json-patch+json"

and in the body you should have something like this:

[
    {
        "op""add",
        "path""/connectorAttributes/nativeRules",
        "value": [
            "Rule 1",
            "Rule 2"
        ]
    }
]

 

where "Rule 1" and "Rule 2" are the names of your rules.

Hi Team,

I can able to fetch all the application arguments with below command.
$applicationInformation=($env:Application);
It is printing as a String but the expected was Map.
How can I store the Application attributes in Map & how can I fetch only required attributes from the entire map?
For example, I need iqservicehost & port alone from the map
Please advise.

Thanks,
Sasitharan Duraisamy.

Hi all,

 

The Rule is getting executed even after the account creation results in a fail. Is this expected? if so, is there a possibility to not execute the rule after an creation error?

 

Thanks,

Hi @Ola5 & @yunus_ali ,

Can anybody help me with the process of writing the connector rule with powershell script in the source and then tagging the rule to Active Directory Source ?

Thanks in advance

Here's the code I used. I found it somewhere on this platform.

$logDate = Get-Date -UFormat %Y%m%d
$logFile = "c:\SailPoint\Scripts\Logs\ConnectorAfterCreate_$logDate.log"
$command = "C:\Temp\AfterCreateScript.ps1\"
$enableDebug = $true

#====================-------Helper functions-------====================
function LogToFile([String] $info) {
    $info | Out-File $logFile -Append
}
    
#====================-------Get the request object-------====================
Try{
    if($enableDebug) {
        LogToFile("Entering SailPoint rule")
    }
    Add-type -path "C:\SailPoint\IQService\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);
    $requestAsString = $env:Request
    if($enableDebug) {
        LogToFile("Request as XML object is: $requestAsString")
    }

    #Call the client script
    $command = -join ($command, " -requestString '$requestAsString'")
    Invoke-Expression $command
}
Catch {
    $ErrorMessage = $_.Exception.Message 
    $ErrorItem = $_.Exception.ItemName
    LogToFile("Error: Item = $ErrorItem -> Message = $ErrorMessage")
}
if($enableDebug) {
    LogToFile("Exiting SailPoint rule")
}

 

 

You need to convert that to JSON to get something like this, which is what you need for the connector rule:

"$logDate = Get-Date -UFormat %Y%m%d\r\n$logFile = \"c:\\SailPoint\\Scripts\\Logs\\ConnectorAfterCreate_$logDate.log\"\r\n$command = \"C:\\Temp\\AfterCreateScript.ps1\\\"\r\n$enableDebug = $true\r\n\r\n#====================-------Helper functions-------====================\r\nfunction LogToFile([String] $info) {\r\n    $info | Out-File $logFile -Append\r\n}\r\n    \r\n#====================-------Get the request object-------====================\r\nTry{\r\n    if($enableDebug) {\r\n        LogToFile(\"Entering SailPoint rule\")\r\n    }\r\n    Add-type -path \"C:\\SailPoint\\IQService\\Utils.dll\\\";\r\n    $sReader = New-Object System.IO.StringReader([System.String]$env:Request);\r\n    $xmlReader = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sReader));\r\n    $requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);\r\n    $requestAsString = $env:Request\r\n    if($enableDebug) {\r\n        LogToFile(\"Request as XML object is: $requestAsString\")\r\n    }\r\n\r\n    #Call the client script\r\n    $command = -join ($command, \" -requestString \u0027$requestAsString\u0027\")\r\n    Invoke-Expression $command\r\n}\r\nCatch {\r\n    $ErrorMessage = $_.Exception.Message \r\n    $ErrorItem = $_.Exception.ItemName\r\n    LogToFile(\"Error: Item = $ErrorItem -\u003e Message = $ErrorMessage\")\r\n}\r\nif($enableDebug) {\r\n    LogToFile(\"Exiting SailPoint rule\")\r\n}"


Then create a connector rule using POST on "https://tenant.api.identitynow.com/beta/connector-rules". In the body of the request, you would have something like this:

{
    "description": "This rule Executes Post-Creation Tasks",
    "type": "ConnectorAfterCreate",
    "signature": {
        "input": [],
        "output": null
    },
    "sourceCode": {
        "version": "1.0",
        "script": "your JSON code goes in here"
    },
    "attributes": {
        "ObjectOrientedScript": "true",
        "extension": ".ps1",
        "sourceVersion": "1.0",
        "disabled": "false",
        "program": "powershell.exe",
        "timeout": "360"
    },
    "name": "Call it whatever you like. E.g:Connector After Create"
}

 

As a final step, you need to attach the rule to your source's connector. The same step described above by @yunus_ali . More detail here: https://developer.sailpoint.com/apis/v3/#operation/updateSource 

Hope this helps.

Hi All,

 

I am trying to invoke the powershell script via the AfterCreate rule and for some reason i am getting error with the script. I am seeing below error in the IQtrace log.

DEBUG : "Script return code : 255"
04/26/2022 16:23:11 : AbstractConnector [ Thread-11 ] DEBUG : "EXIT executeScript"
04/26/2022 16:23:11 : AbstractConnector [ Thread-11 ] DEBUG : "After script returned non zero exit code : 255 : "
04/26/2022 16:23:11 : AbstractConnector [ Thread-11 ] DEBUG : "EXIT executePostScript"

 

Is there any way to identify the error code. Also, the powershell i am invoking i am just trying to send a notification to manager for user AD password.

Hello @anuragt, curious if you ever found a solution to the error 255? We're seeing the same error messages when attempting to run the post create script.

 

Thanks.

Hello @anuragt , @dustin_yeager @anuragt ,

Did you find the solution for non zero exit code : 255 ?

Thanks,

IAM-PDU

 

For those getting the 255 error @anuragt @brent756 @IAMpdu, have you added the attributes to rule?

   "attributes": {
        "ObjectOrientedScript""true",
        "extension"".ps1",
        "disabled""false",
        "program""powershell.exe",
        "timeout""300"
  }
Version history
Revision #:
11 of 11
Last update:
‎Sep 10, 2021 06:24 PM
Updated by:
 
Contributors