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

Hybrid exchange provisioning via PSS script

Hybrid exchange provisioning via PSS script

Hey Sailors, 

Hope you are healthy and safe during these uncertain and unprecedented times.

Running Powershell directly via the IQService. This is a Native Rule to invoke PowerShell which creates RemoteUserMailbox for clients that are in Hybrid Environment after the Active Directory account is created.

We need to send Exchange Attributes [i.e. username, password in text] to Provisioning Plan as attributes map in Account Request that is used while establishing a Remote PSS connection to exchange server.

 

public static Attributes<String, String> getExchangeAttributes(SailPointContext context, String flow)
			throws GeneralException {
		Custom custom = context.getObjectByName(Custom.class, "Customobject where Exchange credentials are saved");
		Attributes<String, String> attributes = new Attributes<String, String>();
		attributes.put("flow", flow);
		attributes.put("username", custom.get("SVC_UserName"));
		attributes.put("pwdText", context.decrypt((String) custom.get("SVC_Password")));
		return attributes;
	}

 

  • The above method can be created in Rule Library or Utility class file 

 

 

 acctReq.setArguments(CORPUtil.getExchangeAttributes(context,"Joiner"));

 

  • Add this arguments map in your plan to Enable and Disable operations whichever suits your organization need

 

ConnectorAfterCreate

Here some example code for the ConnectorAfterCreate PowerShell script that can be invoked after AD account creation.

 

 

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="RemoteMailbox-Create-PSScript" 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="150"/>
    </Map>
  </Attributes>
  <Description>
    An IdentityIQ Server-Side rule that is executed AFTER the connector's provisioning method is called. 
    This rule is called after accounts have been created on the underlying AD domain.
  </Description>
  <Signature>
    <Inputs>
      <Argument name="log">
        <Description>
          The log object associated with the SailPointContext.
        </Description>
      </Argument>
      <Argument name="context">
        <Description>
          A sailpoint.api.SailPointContext object that can be used to query the database if necessary.
        </Description>
      </Argument>
      <Argument name="plan">
        <Description>
          The ProvisioningPlan object on its way to the Connector.
        </Description>
      </Argument>
      <Argument name="application">
        <Description>
          The application object that references this before/after script.
        </Description>
      </Argument>
    </Inputs>
  </Signature>
  <Source><![CDATA[


$logFile = "C:\ExchangeLogs\AfterCreateLogs\$(Get-Date -format "yyyy-MM-dd").ConnectorAfterCreate.log"

Function writeToLog() {
    
  param([string]$message)

  Add-Content ($(Get-Date -format "yyyy-MM-dd HH:mm:ss") + "::$message") -Path $logFile
  
}
  
Function sendEmail{   

  $emailMessage = New-Object System.Net.Mail.MailMessage

  $emailMessage.From = "John Smith <john@CORP.com>"

  $emailMessage.To.Add("akhilreddy@CORP.com" )

  $emailMessage.Subject = "User mailbox  creation failed for User -$sAMAccountName"

  $emailMessage.IsBodyHtml = $true

  $emailMessage.Body = @"

  <p>Hello Team,</p>

  <p>The usermailbox creation for user -$sAMAccountName has falied due to following erros.</p>

  <p> $dn on server: $exchangeServer . Error::  $_.FullyQualifiedErrorId $_.Exception.Message  </p>

  <p>Please take neccessary actions to create an mailbox for the individual immediately .</p>
    
  <p>Best Regards,</p>
          
  <p>-Akhil Reddy</p>
  "@

  $SMTPClient = New-Object System.Net.Mail.SmtpClient( "yourSMTPServer -FQDN/IP" , "port" )

  $SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $username , $pwdText );

  $SMTPClient.Send( $emailMessage)
}

#Notify that we are in afterscript
    
writeToLog("Running AfterCreate Script: 'CORP-Rule-AfterCreateScript'`r`n");

#Refer to SailPoint class library
  
Add-type -path "C:\IQService\utils.dll"

#Read the environment variables

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

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

##Form the xml reader object###
  
$xmlReader = [System.xml.XmlTextReader]([sailpoint.Utils.xml.XmlUtil]::getReader($sReader));

$xmlReader_Result = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sResult));

###Create SailPoint Request object
  
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
  
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult($xmlReader_Result);
  
##Done for inclusion in Log File  
 
$requestXML = $requestObject.toxml();

$resultXML = $resultObject.toxml();

writeToLog("Request Object :: $requestXML");

writeToLog("Result Object  :: $resultXML");

$objectType = ""


if($resultObject.Errors.count -eq 0){
      foreach ($attribute in $requestObject.AttributeRequests){
        if($attribute.Name -eq "ObjectType"){
          $objectType = $attribute.value
        }
      if($attribute.Name -eq "op"){
        $op = $attribute.value
        }
        if($attribute.Name -eq "sAMAccountName"){
          $samAccountName = $attribute.value
        }
        if($attribute.Name -eq "userPrincipalName"){
          $upn = $attribute.value
        }
      }

if($objectType -eq "User"){
  
foreach ($resultEntry in $resultObject.Attributes.GetEnumerator()){
      
  if($resultEntry.Name -eq "createdOnServer"){

  $createdOnServer = $resultEntry.Value

  }
      
}

  
foreach ($requestEntry in $requestObject.Attributes.GetEnumerator()){

  if($requestEntry.Name -eq "username"){
    $username = $requestEntry.Value
    }

      if($requestEntry.Name -eq "pwdText"){
        $pwdText = $requestEntry.Value
        }

            if($requestEntry.Name -eq "flow"){
            $flow = $requestEntry.Value
            }
}


$dn = $requestObject.NativeIdentity;
$op= $requestObject.Operation

writeToLog("//********************* Main Entry Point *******************************//");


writeToLog("Request attributes ---- dn: $dn, ObjectType: $objectType, op : $op, createdOnServer: $createdOnServer, sAMAccountName: $sAMAccountName, upn: $upn, flow:$flow ");

if( $flow -eq "Joiner" -And $op -eq"Create"){
      
$sApplication = New-Object System.IO.StringReader([System.String]$env:Application);

$xmlReader_Application = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sApplication));

[System.Xml.XmlDocument]$document = new-object System.Xml.XmlDocument

$document.load($xmlReader_Application)

$exchangeServers = $document.SelectNodes("//Map/entry[@key='ExchHost']/value/List/String")

$exchangeServersString = $exchangeServers.InnerText

writeToLog("Exchange servers is :::::::::::::::::::::::::$exchangeServersString")


foreach ($exchServerAddress in $exchangeServers) {

Try {

$exchServerAddressString  = $exchServerAddress.InnerText

writeToLog("Trying exchange server: $exchServerAddressString")

$hostInfo = [System.Net.Dns]::GetHostByName($exchServerAddressString)

$exchangeServer = $hostInfo.HostName

$IsOnline = Test-Connection -ComputerName $hostInfo.HostName -BufferSize 16 -Count 1 -Quiet

writeToLog("is server online::::::::::::::::::::: $IsOnline ")

if ($IsOnline -eq $true){

Function Login {

$secpasswd = ConvertTo-SecureString $pwdText -AsPlainText -Force

$credObject = New-Object System.Management.Automation.PSCredential($username, $secpasswd)		

$uri = "http://"+ $exchangeServersString + ":80/PowerShell/"

$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $uri -Credential $credObject -Authentication Kerberos #-ErrorAction Stop

Import-PSSession $session -AllowClobber -DisableNameChecking	

$session

}

$session = Login

writeToLog("Session value: $session")

writeToLog("Invoking Enable-RemoteMailbox command for account: $samAccountName")

set-adserversettings -viewentireforest $true

$email = $samAccountName +"@CORP365.mail.onmicrosoft.com"

writeToLog("email:::::::::::::::::::::::$email")

writeToLog("Executing second cmd for account: $samAccountName")

Enable-RemoteMailbox $samAccountName  -RemoteRoutingAddress $email 

$logger = Get-RemoteMailbox -Identity $samAccountName 

writeToLog("checking mailbox print the display name of the user :: $logger")

writeToLog("cmdlets execution completed")
  
$id =$session.ID

Remove-PSSession -ID $session.ID
    
writeToLog("After session remove: $Session")
$scriptExecuted=$true
}
else{

writeToLog("Cannot connect to server : $exchServerAddressString")
  
}
if($scriptExecuted){
  
writeToLog("Mailbox script execution completed for ::: $samAccountName on server ::: $exchServerAddressString")
  
}

} catch {

writeToLog("Cannot create mailbox features for user: $dn on server: $exchangeServer . Error:  $_.FullyQualifiedErrorId $_.Exception.Message")

sendEmail
  
}
finally {
 
if($session){
  

  
writeToLog(":::::Finally:::")

Remove-PSSession -ID $session.ID
  
##Disconnect-PSSession -ID $session.ID
  
  }
  
}
 

}

}
else{
  
writeToLog("::::--!!  skipping the after create scripts execution as the plan would be from the identityRefreshExecutor !!--:::::")
  
}

 
 } else {

writeToLog("Skipping mailbox creation. After Create script triggered for ObjectType: $objectType")

} 
  
  
} else {

foreach ($errorMsg in $resultObject.Errors){
writeToLog("Errors from Service Result :::`n`r $errorMsg ");	
  
}

}

writeToLog("Executing After Create Script completed for User: $samAccountName");

writeToLog("//*********************End of Script *******************************//");

  ]]></Source>
</Rule>

 

  • The above script connects via Remote PSSession and executes the script for enabling the mailbox and it also sends an email notification upon failures while creating mailbox
  • The above snippet can be used with some modifications as AfterModifyScript - by changing the flow and powershell command for disabling and hiding the mail address.

 

 

 

Set-RemoteMailbox $sAMAccountName -HiddenFromAddressListsEnabled $true

 

  • This will make sure that the disabled user's mail id is hidden from the directory.
Comments

HI @Akhil

Currently, we have on perm exchange server for mailbox but we are moving completely to Azure AD. What will be the best option to create a mailbox in the Azure environment?   Current architecture  -> Create an account in On perm AD and then it gets synched to Azure AD. 

Great article...!!!

I understand this article was posted long before. Please clarify my questions.

OUR ARCHITECTURE: We have an on-prem Active Directory integrated and an on-prem Exchange currently integrated with our IIQ 8.2. We also have an Exchange Online application, it is not connect to IIQ. 

CURRENT BEHAVIOUR: For users who request Exchange accounts, we create an Active Directory account for them on the prem instance, set their mailnickname and in turn Exchange creates their mailboxes on on-prem Exchange. Post Exchange account creation, a backend script runs and migrates the account to Exchange Online

REQUIREMENT: We would like create (enable) remote mailboxes for those users who request Exchange accounts directly from IDM. We do not want to create accounts on on-prem Exchange application

We have stopped creating on-prem Exchange accounts. I am using the rule in this article to enable remote mailboxes post AD account creation but the rule execution fails with the below error

08/08/2022 04:50:14 : AbstractConnector [ Thread-5 ] DEBUG : "Started process"
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] DEBUG : "Script return code : 1"
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] DEBUG : "EXIT executeScript"
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] DEBUG : "After script returned non zero exit code : 1 : "
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] DEBUG : "EXIT executePostScript"
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] WARN : "Create operation is successful but post script execution failed : After script returned non zero exit code : 1 : "
08/08/2022 04:50:16 : AbstractConnector [ Thread-5 ] DEBUG : "EXIT Provision"

Can someone please help me understand why the execution fails?

@darylclaude_medina @Akhil 

Hi, is there a version of this that can be used for IdentityNow?

What a nice article, ty for this information:)

But i have some questions...

Isn't it bad practice to enter the password in the acount request?
It will be recorded in the identityRequest in clear text...

Is there another way to pass credentials securely?

Hi

 

After adding the Account request with IQservice username and password, the joiner workflow fails at Identity Initialize step, at the buildingapproval step with null exception. Not sure what to do.

Version history
Revision #:
3 of 3
Last update:
‎Mar 15, 2023 04:39 PM
Updated by: