Write Back Email, Username and Phone to SAP HR Using SailPoint IIQ
Published March 15, 2019
Insight summary and table of contents
Summary
Contents
Frustrated with writing back new username and email to SAP HR? You're not alone.
Most joiner processes for new hires generate an username and email address dynamically, but a problem can arise when using SAP HR systems because there is no direct way to write back the newly generated username and email to SAP HR 105 records. SailPoint IdentityIQ provides a simple solution. The rule below can be used to write back to SAP HR using IIQ.
Pre-Requisite
The SAP service account should have permission to update the SAP HR records. Please refer to SailPoint connector reference guide for SAP HR configuration.
Steps
- Import the rule to SailPoint
- Edit application SAP HR and goto Rules-> Connector Rules -> Select Radio By Operation Rule-> Select the Rule name "SAP HR Provisioning Rule" as Modify Provision Rule.
- Go to Identity Mapping -> Edit Email attribute -> Set Target Mapping with SAP "Email" Attribute.
- Go to Identity Mapping -> Edit UserName attribute -> Set Target Mapping with SAP "System user name (SY-UNAME)" Attribute.
- Go to Identity Mapping -> Edit Phone attribute -> Set Target Mapping with SAP "Telephone" Attribute.
- Enable attribute sync in the Refresh Identity Cube and execute.
- The modify plan will be generated for the newly created identities and it will call the below rule to write back to SAP.
Rule
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="SAP HR Provisioning Rule" type="SapHrOperationProvisioning">
<Description>This rule is used by the SAP HR connector for provisioning of the data .</Description>
<Signature returnType="ProvisioningResult">
<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="application">
<Description>The application whose data file is being processed.</Description>
</Argument>
<Argument name="schema">
<Description>The Schema currently in use.</Description>
</Argument>
<Argument name="destination">
<Description>A connected/ready to use
com.sap.conn.jco.JCoDestination object that can be used to call
bapi, function modules and call to SAP tables. This is the main
object used in making BAPI calls using the JCo interface.This
destination object is shared with the connector implementation and
the connector controls the destination lifecycle.</Description>
</Argument>
<Argument name="plan">
<Description>The ProvisioningPlan created against the SAP
application.</Description>
</Argument>
<Argument name="request">
<Description>The ProvisioningRequest created against the SAP
application.</Description>
</Argument>
<Argument name="connector">
<Description>The SAP connector that is being used to communicate
with SAP. This class is here for convenience as there are many
utility methods that make calling Function Modules and doing table
lookup easier.</Description>
</Argument>
</Inputs>
<Returns>
<Argument name="result">
<Description>A Provisioning Result object is desirable to return the
status.IT can be a new object or part of Provisioning Plan</Description>
</Argument>
</Returns>
</Signature>
<Source>
import sailpoint.object.*;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningPlan.ObjectOperation;
import sailpoint.object.ProvisioningPlan.AccountRequest.Operation;
import sailpoint.tools.Util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import com.sap.conn.jco.AbapException;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException;
import sailpoint.connector.ConnectorException;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoParameterField;
import com.sap.conn.jco.JCoParameterFieldIterator;
import com.sap.conn.jco.JCoParameterList;
import com.sap.conn.jco.JCoStructure;
import com.sap.conn.jco.JCoTable;
import com.sap.conn.jco.JCoContext;
import com.sap.conn.jco.*;
import sailpoint.api.*;
import sailpoint.transformer.*;
import sailpoint.tools.*;
import sailpoint.tools.Message.Type.*;
import sailpoint.connector.*;
import java.text.*;
// subtype values for email , Telephone or system user name
String SUBTYPE_EMAIL = "0010"; //Sub info type for email address
String SUBTYPE_PHONE= "0020"; //Sub info type for telephone number
String SUBTYPE_SY_USERNAME= "0001"; //Sub info type for sy-username which can be mapped to SAMAccountName of AD or RACFID
ProvisioningResult result = new ProvisioningResult();
//This function will modify the SAP User data email address , Telephone or system user name if recieved in the plan
public void doProvision() throws Exception
{
List<AccountRequest> accReqList = plan.getAccountRequests();
String accNativeIdentity = null;
String beginDateStr ="" ;
String endDateStr ="" ;
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
if (!Util.isEmpty(accReqList))
{
for( AccountRequest accountReq : accReqList )
{
result.setStatus( ProvisioningResult.STATUS_COMMITTED );
accNativeIdentity = accountReq.getNativeIdentity();
// For update operation only Email,Telephone,SY-UNAME atrribute are supported
AttributeRequest emailAttr = accountReq.getAttributeRequest("Email");
AttributeRequest phoneAttrib = accountReq.getAttributeRequest("Telephone");
AttributeRequest syUserAttrib = accountReq.getAttributeRequest("System user name (SY-UNAME)");
HashMap beginEndDateList = getCommunicationData(accNativeIdentity);
//Finding the email attribute in provisioning plan and trying to modify the account's email id
if ( null != emailAttr )
{
if(null !=beginEndDateList && beginEndDateList.containsKey("emailBegin"))
{
beginDateStr = formatter.format(beginEndDateList.get("emailBegin"));
endDateStr = formatter.format(beginEndDateList.get("emailEnd"));
}
modifyCommunicationData(accNativeIdentity, emailAttr.getValue(), SUBTYPE_EMAIL,beginDateStr,endDateStr);
}
//Finding the Telephone attribute in provisioning plan and trying to modify the account's phone
if ( null != phoneAttrib )
{
if(null !=beginEndDateList && beginEndDateList.containsKey("phoneBegin"))
{
beginDateStr = formatter.format(beginEndDateList.get("phoneBegin"));
endDateStr = formatter.format(beginEndDateList.get("phoneEnd"));
}
modifyCommunicationData(accNativeIdentity, phoneAttrib.getValue(), SUBTYPE_PHONE,beginDateStr,endDateStr);
}
//Finding the system user name attribute in provisioning plan and trying to modify the account's System User Name
if ( null != syUserAttrib)
{
if(null !=beginEndDateList && beginEndDateList.containsKey("syUserBegin"))
{
beginDateStr = formatter.format(beginEndDateList.get("syUserBegin"));
endDateStr = formatter.format(beginEndDateList.get("syUserEnd"));
}
modifyCommunicationData(accNativeIdentity,syUserAttrib.getValue(), SUBTYPE_SY_USERNAME,beginDateStr,endDateStr);
}
}
}
}
// This function will get the communication details about a given employee
// In this example the email, Telephone and system user name begin and end date will be retrieved
public HashMap getCommunicationData( String id ) throws Exception
{
HashMap commAttrsList = new HashMap();
JCoFunction getCommDetail = connector.getFunction(destination, "BAPI_EMPLCOMM_GETDETAILEDLIST");
getCommDetail.getImportParameterList().setValue("EMPLOYEENUMBER", id);
getCommDetail.getImportParameterList().setValue("TIMEINTERVALLOW", new Date());
getCommDetail.getImportParameterList().setValue("TIMEINTERVALHIGH", new Date());
try
{
getCommDetail.execute(destination);
} catch (Exception e)
{
connector.checkForExceptions(getCommDetail);
throw new Exception(e);
}
JCoTable commTable = getCommDetail.getTableParameterList().getTable("COMMUNICATION");
if ( commTable != null )
{
int rows = commTable.getNumRows();
for (int i = 0; i < rows; i++)
{
commTable.setRow(i);
String commType = commTable.getString("SUBTYPE");
commAttrsList = getDateData(commTable, commAttrsList, commType);
}
}
//log.error("commAttrsList :::"+commAttrsList);
return commAttrsList;
}
// function captures the begin date and end date
public HashMap getDateData(JCoTable commTable, HashMap commAttrsList, String commType)
{
Date validBegDate = commTable.getDate("VALIDBEGIN");
Date validEndDate = commTable.getDate("VALIDEND");
if ( validBegDate != null && validEndDate != null )
{
if (commType.equals(SUBTYPE_EMAIL))
{
commAttrsList.put("emailBegin", validBegDate);
commAttrsList.put("emailEnd", validEndDate);
} else if (commType.equals(SUBTYPE_PHONE))
{
commAttrsList.put("phoneBegin", validBegDate);
commAttrsList.put("phoneEnd", validEndDate);
}else if (commType.equals(SUBTYPE_SY_USERNAME))
{
commAttrsList.put("syUserBegin", validBegDate);
commAttrsList.put("syUserEnd", validEndDate);
}
}
return commAttrsList;
}
/**Function modifies the email address , Telephone number and System user name of SAP HR record.
If Email or Phone is present(assigned) then used BAPI_EMPLCOMM_CHANGE
If Email or Phone is not present(assigned) then used BAPI_EMPLCOMM_CREATE
* @param userId
* @param parValue
* @param type
* @param begDate
* @param endDate
* @throws ConnectorException
*/
private void modifyCommunicationData( String userId, String parValue, String type,String begDate,String endDate ) throws ConnectorException
{
JCoFunction jcoFunctionObject;
if (begDate.length() > 1 ) { //If date is alreday present then use BAPI_EMPLCOMM_CHANGE to modify data
jcoFunctionObject = connector.getFunction(destination,"BAPI_EMPLCOMM_CHANGE");
} else { //If date is not present then use BAPI_EMPLCOMM_CREATE to add data
jcoFunctionObject = connector.getFunction(destination,"BAPI_EMPLCOMM_CREATE");
}
//log.error("userId::"+userId);
//log.error("parValue::"+parValue);
//log.error("type::"+type);
//log.error("begDate::"+begDate);
//log.error("endDate::"+endDate);
if(begDate.equals(""))
{
Date today = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(today);
cal.add(Calendar.DATE, -1);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
begDate = sdf.format(cal.getTime());
}
if(endDate.equals(""))
{
endDate = "99991231";
}
// BAPI locks the record for processing
JCoFunction functionEnqueue = destination.getRepository().getFunction("BAPI_EMPLOYEE_ENQUEUE");
functionEnqueue.getImportParameterList().setValue("NUMBER", userId);
if ( functionEnqueue == null )
throw new RuntimeException("BAPI_EMPLOYEE_ENQUEUE not found in SAP.");
// BAPI to modify Communication data - email and phone
if ( jcoFunctionObject == null )
throw new RuntimeException("BAPI_EMPLCOMM_CHANGE not found in SAP.");
String returnPersonnelID = null;
jcoFunctionObject.getImportParameterList().setValue("EMPLOYEENUMBER", userId); // Personal Number
jcoFunctionObject.getImportParameterList().setValue("SUBTYPE", type); // SubType 0010/0020 - Email/Phone
jcoFunctionObject.getImportParameterList().setValue("VALIDITYBEGIN", begDate); // Begin Date
jcoFunctionObject.getImportParameterList().setValue("VALIDITYEND", endDate); // End Date
jcoFunctionObject.getImportParameterList().setValue("COMMUNICATIONID", parValue); // Email Address to modify
// BAPI unlocks the record after processing
JCoFunction functionDequeue = destination.getRepository().getFunction("BAPI_EMPLOYEE_DEQUEUE");
functionDequeue.getImportParameterList().setValue("NUMBER", userId);
if ( functionDequeue == null )
throw new RuntimeException("BAPI_EMPLOYEE_DEQUEUE not found in SAP.");
try {
// executing Bapis
JCoContext.begin(destination);
functionEnqueue.execute(destination);
jcoFunctionObject.execute(destination);
//log.error("Function executed successfully");
functionDequeue.execute(destination);
} catch (ConnectorException e) {
throw e;
} finally {
JCoContext.end(destination);
}
}
//////////////////// BEGIN CODE ///////////////////
doProvision(); // UPDATE SAP HR EMAIL and RACFID//
//////////////////// END CODE ///////////////////
return result;</Source>
</Rule>
We hope this helps as you work with SAP HR and SailPoint IdentityIQ.