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.