Powershell Script for afterModify examples

Morning all,
I am trying to find a way to add AD entitlements where the naming convention is based upon identity attributes. ie: ‘$number $type’.
I think that the only way to do this is via a ConnectorAfterModify rule to kick off a powershell script. I’ve read the extensive documentation, but the problem is my lack of PS experience.
Does anyone have examples of scripts that they are happy to share here so that I can understand the structure and build out my own solution?
Many thanks
Phil

1 Like

You have an example here : Before and After Operations on Source Account Rule | SailPoint Developer Community

Rule template :

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
 <Rule language="beanshell" name="SampleSource AfterModify" type="ConnectorAfterModify">
  <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 to
            initiate a separate PowerShell script residing on the client's IQService server.

            Configuration tasks include the following:
             - 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>

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\SampleSourceBeforeCreateScript_$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 beforeScript")
}

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 beforeScript")
}
1 Like

Thank you for your response @baoussounda. It looks like I wasn’t clear in my first post.
This is the part that I’m stuck on, and I was wondering if any had any examples of function code that they wouldn’t mind sharing, that I would be able to build upon:

# 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

Hi @phil_awlings,

Here is a basic structure that you can work on

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

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

$nativeIdentity = $requestObject.nativeIdentity
if ($requestObject.AttributeRequests ) {
	foreach ( $attribute in $requestObject.AttributeRequests ) {
		if ( $attribute.Name ) {
			if ( $attribute.Name -eq 'employeestatus' ) {
				$employeestatus = Get-AttributeValueFromAccountRequest -request $requestObject -targetAttribute "employeestatus"
				Set-ADUser -Identity $nativeIdentity -Replace @{employeestatus="$employeestatus";mail="[email protected]"}
			}

			else {
				LogToFile("No change required")
			}
		}
	}
}
#
# End Client-provided code
1 Like

Hi
Hoping that someone can add some clarity. I seem unable to pull in the value of an attribute.
The code above suggest that is:

$employeestatus = Get-AttributeValueFromAccountRequest -request $requestObject -targetAttribute "employeestatus"

However I have tried the following (from other threads)

    $sAMAccountName      = Get-AttributeValueFromAccountRequest $requestObject "sAMAccountName"
    $sAMAccountName1     = Get-AttributeValueFromAccountRequest -request $requestObject -targetAttribute sAMAccountName
    $sAMAccountName2     = Get-AttributeValueFromAccountRequest -request $requestObject -targetAttribute "sAMAccountName"

None of which work. I have tried having the object as an attribute name from both the Identity and the source, just in case, and that did not work either

Thanks in advance

Hi @phil_awlings,

Have you added the below function in your code.? Also, the target attribute should be in your modify plan.

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;
}

Also, this can be another option instead of using the function

($request.AttributeRequests | Where-Object Name -eq $targetAttribute).Value

Hi @jesvin90
I have that function in the code.

I just amended the object to the attribute that changed and that worked.
Thank you

Does this mean that the only values that I can get from the function(Get-AttributeValueFromAccountRequest) are ones that have been modified? All other attributes are not available via that method, but instead I need to call them using something like this instead?

$user = Get-ADUser -identity $nativeIdentity -Properties  samaccountname, userType

Phil

Hi @phil_awlings,

Yes. In the after scripts, you will only be able to get the attributes that are available in the plan.

Else you will need a before provisioning rule to add extra attributes to the plan and then call it in the AfterModify script.

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