Aftercreate Rule and Powershell script to generate password and send email to user's manager

Hi Everyone,

I’m trying to create an aftercreate rule for an AD source in IDN, I will copy the rule and powershell script I have built so far, but I see an error in IQservices powershell script like follows: Error: Item = → Message = Exception calling “.ctor” with “1” argument(s): “Data at the root level is invalid. Line 1, position 1.”

Anyone ran into this issue?

TIA

Also if you could post the working rule and powershell script, would really appreiate it. Thanks.
Powershell Script:

###############################################################################################################################
# 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
)

#import AD cmdlets
Import-Module activeDirectory

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

#log file info
$logDate = Get-Date -UFormat "%Y%m%d"
$logFile = "C:\SailPoint Scripts\PS Script Logs\ConnectorAfterCreate_$logDate.log"
$enableDebug = $true

###############################################################################################################################
# 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 PS")
}

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
if($enableDebug) {
    LogToFile("Entered logic...1")
}
	
	$nativeIdentity = $requestObject.nativeIdentity
		
		# Generate a random password
		$randomPassword = "RandomPassword@123456!"

if($enableDebug) {
    LogToFile("Entered logic...2")
}
		
		# Set the password in AD
		Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset 

		# Output the new password to the SailPoint event log
		# Write-Host "New password for ${nativeIdentity}: $randomPassword"
			
}
catch {
    $ErrorMessage = $_.Exception.Message
   $ErrorItem = $_.Exception.ItemName
   LogToFile("Error: Item = $ErrorItem -> Message = $ErrorMessage")
}

if($enableDebug) {
    LogToFile("Exiting AfterScript PS")
}

AfterCreate Rule:

$logDate = Get-Date -UFormat "%Y%m%d"
$logFile = "C:\SailPoint Scripts\PS Script Logs\ConnectorAfterCreate_$logDate.log"
$command = "C:\SailPoint\ADSource-AfterCreate.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:\DM\Tools\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 = $requestObject.nativeIdentity

    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")
}

Did you convert this into a Json Object?

Here is an example of working call for PowerShell.

{
    "description": "This ConnectorAfterCreate will enable exchange online mailbox.",
    "type": "ConnectorAfterCreate",
    "signature": {
        "input": [],
        "output": null
    },
    "sourceCode": {
        "version": "2021-03-05 15:26:29",
        "script": "\n\n\t\t#$logDate = Get-Date -UFormat \"%Y%m%d\"\n\t\t\t#$logFile = \"C:\\IDNProvisioningScripts\\IDNProvisioningLogs_$logDate.log\"\n\t\t\t$command = \"C:\\IDNProvisioningScripts\\IDN-MailProvisioning.ps1\"\n\t\t\t$generate_guid = \"C:\\IDNProvisioningScripts\\IDN-ImmutableID.ps1\"\n\t\t\t$enableDebug = $true\n\t\t\t[string]$LogSource = \"IDNScripts\"\n\t\t#====================-------Helper functions-------====================\n\t\t        Function LogWrite\n\t\t\t{\n\t\t\t\tParam (\n\t\t\t\t\t[string]$LogString\n\t\t\t\t\t ,\n\t\t\t\t\t[string]$EntryType\n\t\t\t\t\t ,\n\t\t\t\t\t[int]$EventID\n\t\t\t\t\t ,\n\t\t\t\t\t[string]$Source = 'Unspecified'\n\t\t\t\t\t ,\n\t\t\t\t\t[boolean]$DebugLog = $false\n\t\t\t\t)\n\t\n\t\t\t\t[boolean]$logExists = Get-EventLog -list | Where-Object { $_.log -eq \"SSMScripts\" }\n\t\n\t\t\t\tif (! $logExists)\n\t\t\t\t{\n\t\t\t\t\tNew-EventLog -LogName SSMScripts -Source \"User\", \"Computer\", \"Other\", \"Admin\", $Source\n\t\t\t\t\tLimit-Eventlog -OverflowAction OverwriteAsNeeded -LogName \"SSMScripts\" -MaximumSize 2048kb\n\t\t\t\t\tLogWrite -EntryType 'Information' -Source 'Admin' -EventID 9999 -logstring \"SSM Script Log Initialized\"\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (! ([System.Diagnostics.EventLog]::SourceExists($Source)))\n\t\t\t\t\t{\n\t\t\t\t\t\tNew-EventLog -LogName SSMScripts -Source $Source, \"Admin\" -ErrorAction SilentlyContinue\n\t\t\t\t\t\tLogWrite -EntryType 'Information' -Source 'Admin' -EventID 9999 -logstring \"Added New Source To Event Log: $Source\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($DebugLog -eq $true) { Write-Host $LogString } #Write to screen\n\t\n\t\t\t\t[string]$LogWrite = ($LogString)\n\t\n\t\t\t\tWrite-EventLog -LogName SSMScripts -Source $Source -EntryType $EntryType -EventID $EventID -Message $LogWrite\n\t\n\t\t}\n\t\t#====================-------Get the request object-------====================\n\t\t        Try{\n\t\t            if($enableDebug) {\n\t\n\t\t                LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"Entering SailPoint Exchange Online rule\"\n\t\t            }\n\t                try\n\t                {\n\t\t                Add-type -path C:\\Users\\Administrator.LAB3CHILD\\Downloads\\IQService\\utils.dll;\n\t                }\n\t                catch\n\t                {\n\t                    LogWrite -EntryType 'Warning' -Source $LogSource -EventID 1001 -logstring \"Not able to add utils.dll from lab path.\"\n\t\n\t                }\n\t                try\n\t                {\n\t                    Add-type -path E:\\IQService\\utils.dll;\n\t                }\n\t                catch\n\t                {\n\t                    LogWrite -EntryType 'Warning' -Source $LogSource -EventID 1001 -logstring \"Not able to add utils.dll from production path.\"\n\t                }\n\t\t            # Read the environment variables\n\t\t            $sReader = New-Object System.IO.StringReader( [System.String]$env:Request );\n\t\t            $sResult = New-Object System.IO.StringReader( [System.String]$env:Result );\n\t\t            # Form the xml reader object\n\t\t            $xmlReader = [System.xml.XmlTextReader]( [sailpoint.utils.xml.XmlUtil]::getReader( $sReader ) );\n\t\t            $xmlReader_Result = [System.xml.XmlTextReader]( [sailpoint.utils.xml.XmlUtil]::getReader( $sResult ) );\n\t\t            # Create SailPoint Request object\n\t\t            $requestObject = New-Object Sailpoint.Utils.objects.AccountRequest( $xmlReader );\n\t\t            $resultObject = New-object Sailpoint.Utils.objects.ServiceResult( $xmlReader_Result );\n\t\t            $requestAsString = $env:Request\n\t\n\t\t            $createdOnServer = $resultObject.Attributes[\"createdOnServer\"];\n\t\t            LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"Account created on: $createdOnServer\"\n\t\t            # Get sAMAccountName\n\t\t            foreach ( $attribute in $requestObject.AttributeRequests ) {\n\t\t                if ( $attribute.Name -eq \"sAMAccountName\" ) {\n\t\t                    $sAMAccountName = $attribute.Value;\n\t\t                }\n\t\t                if ( $attribute.Name -eq \"memberOf\" ) {\n\t\t                    $memberOf = $attribute.Value;\n\t\t                    $attributeOperation = $attribute.Operation;\n\t\t                }\n\t\t            }\n\t\t            if ( $enableDebug ) {\n\t\n\t\t                LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"Request as XML object is: $requestAsString\"\n\t\n\t\t                LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"sAMAccountName is: $sAMAccountName\"\n\t\t            }\n\t\t            #Call the client script if there's no errors in the results.\n\t\t            foreach($member in $memberOf) {\n\t\t                if ( ( $member -like \"CN=G999-O365-ClinicalWorker-F3*\" -or $member -like \"CN=G999-O365-KnowledgeWorker-E5*\") -and ( $attributeOperation -like \"Add\" ) ) {\n\t\t                    $command = -join ( $command, \" -UserID '$sAMAccountName'  -ssmExch '1'\" )\n\t\t                    $output = Invoke-Expression $command\n\t\n\t\t                    LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"AfterCreate - Customer script Output: $output\"\n\t\t                }\n\t\t            }\n\t\t            $generate_guid = -join ( $generate_guid, \" -UserID '$sAMAccountName'\" )\n\t\t            $generate_guid_output = Invoke-Expression $generate_guid\n\t\t            $timestampString = (Get-Date -Format \"yyyy-MM-dd_HH-mm-ss\").ToString()\n\t\t            LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"AfterCreate - Customer script Output: $generate_guid_output\"\n\t\t        } Catch {\n\t\t            $ErrorMessage = $_.Exception.Message\n\t\t            #$ErrorItem = $_.Exception.ItemName\n\t\t            #LogWrite(\"Error: Item = $ErrorItem -> Message = $ErrorMessage\")\n\t\t            LogWrite -EntryType 'Error' -Source $LogSource -EventID 1002 -logstring \"Error: -> Message = $ErrorMessage\"\n\t\t        }\n\t\t        if ( $enableDebug ) {\n\t\n\t\t            LogWrite -EntryType 'Information' -Source $LogSource -EventID 1001 -logstring \"Exiting SailPoint Exchange Online rule\"\n\t\t        }\n        #$requestObject.toxml() | out-file $logfilename -append\n\n\t"
    },
    "attributes": {
        "ObjectOrientedScript": "true",
        "extension": ".ps1",
        "sourceVersion": "2021-03-05 15:26:29",
        "disable": false,
        "disabled": "false",
        "program": "powershell.exe",
        "timeout": "360"
    },
    "id": "b57382d6f6194594bbca2076db19fd33",
    "name": "AfterCreate Exchange Online",
    "created": "2022-05-05T05:11:39.494Z",
    "modified": "2023-01-24T23:06:23.024Z"
}

Hi @mpotti , No I dont have anything that says convert to JSON, do we have to do it ? if so, can you point me where would it go. Thanks

Here you go. This should help. Please let us know if you have any more questions. Writing you first rule can be quite intimidating.

1 Like

Thanks @mpotti , looks like I have tweaked the sailpoint lines, so it was giving me the error:
I tweaked it to

$sReader = New-Object System.IO.StringReader([System.String]$requestString);

while it should be

$sReader = New-Object System.IO.StringReader([System.String]$env:Request);

not sure why I did that, but after reverting it back to what SP documentation has, I was able to do the operation through powershell.

Thanks for the response though, Mark…