Problem in executing an after create rule with pawershell script

Hello everyone, I’m actually creating an after create rule that call a pawershell script inside the IQService to set the first password of the AD.

First of all I’ve created the after create rule:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
 <Rule language="beanshell" name="SetPassword AfterCreateRule" 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 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\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 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>

Than I uploaded the rule via API trough the POST connector-rules, sending as body this:

{
    "description": "Executes PowerShell commands on the IQService component after a source account is created.",
    "type": "ConnectorAfterCreate",
    "signature": {
        "input": [],
        "output": null
    },
    "sourceCode": {
        "version": "1.0",
        "script": "$logDate = Get-Date -UFormat \"%Y%m%d\"\r\n$logFile = \"C:\\SailPoint\\Scripts\\Logs\\ConnectorAfterCreate_$logDate.log\"\r\n$command = \"C:\\SailPoint\\Scripts\\ADSource-AfterCreate.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\r\n    Add-type -path 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\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 '$requestAsString'\")\r\n    Invoke-Expression $command\r\n\r\n}Catch{\r\n $ErrorMessage = $_.Exception.Message\r\n   $ErrorItem = $_.Exception.ItemName\r\n   LogToFile(\"Error: Item = $ErrorItem -> Message = $ErrorMessage\")\r\n}\r\n\r\nif($enableDebug) {\r\n    LogToFile(\"Exiting SailPoint rule\")\r\n}"
    },
    "attributes": {
        "ObjectOrientedScript": "true",
        "extension": ".ps1",
        "sourceVersion": "1.0",
        "disabled": "false",
        "program": "powershell.exe",
        "timeout": "300"
    },
    "id": "2d93c02498184d83a6fc914a0af8a279",
    "name": "AD_Password_After_Create_Rule"
}

Than I located the powerhsell script inside the folder named C:/Sailpoint/Scripts, the script is named “ADSource-AfterCreate” and this is the code:

###############################################################################################################################
# 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]$env:Request);
        $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
    $nativeIdentity = $requestObject.nativeIdentity
    # Generate a random password
    $randomPassword = "RandomPassword123456!"
    #Set the password in AD
	Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset 

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

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

Than I’ve set the native rule as the name of the after create rule in the AD Connector. The point is , when I’m creating a new AD account and I’m trying to connect to AD with AD username and the password that I’ve set inside the script, the login fails. It seems like the script is not executed, but from the ccg.log of the VA this is the output:


Can anyone experienced with these scripts and rules helps me? Thanks in advance

1 Like

Gm @s_tartaglione ,
Actually the issue is thta you’re using the native identity from the request object.
If the operation is create the AccountRequest has not yet a valid nativeIdentity.
you’ll need to sue antoher attribute such as a DN, samAccountName etc.

Just grab it from the AttributeRequests.

foreach ($attribute in $requestObject.AttributeRequests){
	Switch ($attribute.Name){
		"sAMAccountName"  {$sAMAccountName= $attribute.value }
		"mail" {$email = $attribute.value }
		
	}
}

use this code snippet as a example.

In my case the identity is already created, because I assign the AD account to an identity that hasn’t it. In your suggestion I have to change this code:

get the necessary info we need from the accountRequest object
    $nativeIdentity = $requestObject.nativeIdentity
    # Generate a random password
    $randomPassword = "RandomPassword123456!"
    #Set the password in AD
	Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset 

to the snippet that you have wrote?

1 Like

Also my goal is to set an AD Password, not to set other attributes, how i Can achieve that?

Hi Salvatore!

You should try a very basic logic, make sure it works and then go on. For example, erase all your rule, and let this only line:

New-Item -Path ”c:\test.txt” -ItemType File -Value "working"

if this work, let add some logic until it crashes. Powershell rules does not execute if there is some syntax error (unfortunately, it does not warning this situation).

Other useful tip would be to log after every line of code, so you can check if values does not come empty, etc. For example,

    #get the necessary info we need from the accountRequest object
    $nativeIdentity = $requestObject.nativeIdentity
    LogToFile("native identity is: " + $nativeIdentity)

    # Generate a random password
    $randomPassword = "RandomPassword123456!"
    #Set the password in AD
   Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset 
    LogToFile("ends without exception")

Hi @s_tartaglione,

Some steps to troubleshoot :

  1. Remove the Rule and try running the password reset command vmanually to check if it works.
$randomPassword = "RandomPassword123456!"
Set-ADAccountPassword -Identity "AD DN" -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset
  1. Set $enableDebug = $true in the script. Also add the below code to see if the expected request objects are received. Also, if the script is getting executed you will receive the output logs and that way you can make sure that the script is getting executed
if ($enableDebug) {
		LogToFile("Request object contents:")
		LogToFile($requestObject | Out-String)
	}
  1. Check your IQService logs to see if there are any errors.

Hello.

Try this:

#get the necessary info we need from the accountRequest object
    $nativeIdentity = Get-AttributeValueFromAccountRequest -request $requestObject -targetAttribute "AdSamAccountName" #needs to be a valid sAMAccountName, DistinguishedName, or userPrincipalName.
    # Generate a random password
    $randomPassword = "RandomPassword123456!"
    #Set the password in AD
	Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset 

I’ve tried to execute the command that you sent and i’m getting this error:

The same error that I have in the previos response I have with the test code that you have wrote:

You need to replace the “AD DN” with the user’s distinguished name and see if setting the password works.

I’m still having an authentication error:

Hi @s_tartaglione,

So, it doesn’t look like an issue with your script. This could be more of a permission issue.

  1. Are you running this command on a domain joined machine.?
  2. Can you try Run as Administrator in PowerShell and see if it works
  3. Also, try specifying the server name in your command to see if it works:

Set-ADAccountPassword -Identity "AD DN" -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset -Server "yourdc.domain.com" – Replace the AD DN and server name.

I’ve found the problem because i think that the service user that executes the script hasn’t the right privileges: the service user is : nt authority\system that is not capable to perform the ADSetPassword, while If i’m trying the script logging with my admin credentials inside the domain using directly powershell it’s work. Do you have suggestions on how to change the user while executing the script trough the Create After Rule of Sailpoint or any workaround?

Hi @s_tartaglione,

I would suggest elevating the permission of the SVC account to perform the AD password reset.

If not, you can try running the command as a separate user (Replace the username/password with your values) :

$securePassword = ConvertTo-SecureString "YourSVCAccountPassword" -AsPlainText -Force
$adminCred = New-Object System.Management.Automation.PSCredential("DOMAIN\SVCUserName", $securePassword)

Set-ADAccountPassword -Identity $nativeIdentity -NewPassword (ConvertTo-SecureString -AsPlainText $randomPassword -Force) -Reset -Credential $adminCred

You can also considering encrypting the SVC Account password in an external file and calling it in the script, so that the password is not hardcoded, with something as below :

$adminCred = Get-Credential
$adminCred | Export-Clixml -Path "C:\secure\adadmin.xml"
$adminCred = Import-Clixml -Path "C:\secure\adadmin.xml"

Hello Salvatore,

Please try following

  1. Add the service account to the Administrators/Domain Administrators group.
    Required Permissions

  2. Let the IQservice Log on as “This account” (Services>IQservice>Properties)

  3. Register the service account with IQService. [if already registered you can deregister and register it again]
    Command to degister:
    iqservice.exe - x domain\svaccount
    Command to register:
    iqservice.exe - a domain\svaccount
    Please ensure that exact same username is configured on the source for this feature to work.

  4. Restart the IQService.

Hi, thanks for your response,I’m trying to register my admin user that is able to launch powershell script. When I try to register the account with the IQService it returns this erorr code:

User "..." is not registered for client authentication.

Hi my friend, looks that the user you are logged in does not have permissions. Can you try with an administrator? (only to discard if it works with permissions)

Hi, I’m following your guide, but when I’m trying to restart the IQService I have this error: