2013-05-09

Creating entries to AD (or other LDAP server) from ServiceNow using MuleESB

So you would like to extend the capabilities of the out of the box "New Employee Hire" Order Guide, or your own catalog item's capabilities, to allow you to automatically create users to your AD based on an order from Service Catalog?



If your company has invested in ServiceNow's Orchestration (or Runbook automation as it used to be called), it is your lucky day. Just follow the step-by-step instructions from the ServiceNow wiki and adapt the examples to fit your needs. ServiceNow instance and a MID Server installed into your company's network will provide all the necessary functionality for modifying your AD.

The other people, whose use cases and cost-benefit calculation did not justify buying the Orchestration licenses, can continue reading to see a bare bones example how to create users to your AD from ServiceNow using Mule ESB.


Disclaimer

I could not get my hands on an AD system, so I will use ApacheDS instead of Active Directory in my example. Since both systems support LDAP v3 the example should work against AD, as well as against any LDAP v3 compliant server.

Also, this will not be a step by step instruction as the example would never be enough for production use (e.g. lazy author did not setup HTTPS endpoint and hard coded LDAP user credentials). This example is intended as an idea.

Overview

The overview is simple:
  1. Mule ESB publishes a SOAP over HTTP(S) Web Service, which is called by ServiceNow.
  2. The flow inside Mule ESB will transform provided information into an LDAPEntry
  3. Mule ESB makes LDAP(S) call to ApacheDS (or other LDAP server)
  4. ServiceNow receives response from the Web Service

ServiceNow side

On ServiceNow side you would do the normal steps.
  • Use the Web Service's WSDL to create SOAP Message, SOAP Message Function and SOAP Message Parameters
  • Modify the Catalog Item's workflow by adding a SOAP Message activity which will call the Mule ESB flow's Web Service

MuleESB

Setting up the Web Service is easy.  In MuleStudio you can drag and drop the endpoints into a flow, but the end result is always an XML configuration you could also write by hand.

Web Service (steps 1 and 4)
Global CFX configuration. Nothing fancy here.
<cxf:configuration name="CXF_Configuration" enableMuleSoapHeaders="true" initializeStaticBusInstance="true" doc:name="CXF Configuration"/>
HTTP and SOAP endpoints. SOAP endpoint refers to an JAX-WS annotated Java interface. You can obviously use HTTPS endpoint as well.
<flow name="SNCCreateToLDAPFlow1" doc:name="SNCCreateToLDAPFlow1">
        <http:inbound-endpoint exchange-pattern="request-response" address="http://localhost:8081/createToLDAP" doc:name="HTTP"/>
        <cxf:jaxws-service serviceClass="com.uusoksa.UserCreatorInterface" validationEnabled="true" doc:name="SOAP" configuration-ref="CXF_Configuration"/>
        ...
    </flow>
The Java interface and implementation. In the example we just take in first name and last name, but obviously we could take in any number of fields.

package com.uusoksa;

import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public interface UserCreatorInterface {
    String createUser(@WebParam(name="firstname") String firstname, @WebParam(name="lastname") String lastname);
}
package com.uusoksa;

import javax.jws.WebParam;
import javax.jws.WebService;

@WebService(endpointInterface = "com.uusoksa.UserCreator",
    serviceName = "UserCreator",
    targetNamespace="http://uusoksa.com/userCreator",
    portName="UserCreatorPort")
public class UserCreator implements UserCreatorInterface {
    public String createUser(@WebParam(name="firstname") String firstname, @WebParam(name="lastname") String lastname ) {
        return "";
    }

Transformation (step 2)
The transformation of the data is handled by two separate transformations. First is my own transformer which converts the data into a Map, and the second is an existing transformer which transfers the Map into an LDAPEntry.
<flow name="SNCCreateToLDAPFlow1" doc:name="SNCCreateToLDAPFlow1">
        ...
        <custom-transformer class="com.uusoksa.RequestContentToMap" doc:name="Java"/>
        <ldap:map-to-ldap-entry doc:name="LDAP"/>
        ...
    </flow>
In the referred Java class I am setting some basic information into the Map, as well as the firstname and lastname provided by the web service call.
package com.uusoksa;

import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractTransformer;
import java.util.HashMap;
import java.util.Map;

public class RequestContentToMap extends AbstractTransformer {

    @Override
    protected Object doTransform(Object src, String enc)
            throws TransformerException {
        Object[] objarr = (Object[]) src;
       
        String firstname = (String) objarr[0];
        String lastname = (String) objarr[1];
        System.out.println(firstname+" "+lastname);
        Map<String, String> map = new HashMap<String, String>();
        map.put("givenName", firstname);
        map.put("sn", lastname);
        map.put("cn", firstname+" "+lastname);
        map.put("displayName", lastname+", "+firstname);
        map.put("objectClass", "inetOrgPerson");
        map.put("dn", "cn="+firstname+" "+lastname+",ou=Users,dc=example,dc=com");
       
        return map;
    }
}

Add to LDAP (step 3)
The step 3 consists of a global LDAP config entry...
<ldap:config name="LDAP" authDn="<userdn>" authPassword="<password>" url="ldap://<host:port>" doc:name="LDAP">
        <ldap:connection-pooling-profile initialisationPolicy="INITIALISE_ONE" exhaustedAction="WHEN_EXHAUSTED_GROW"/>
    </ldap:config>
 and LDAP connector.
<flow name="SNCCreateToLDAPFlow1" doc:name="SNCCreateToLDAPFlow1">
        ...
        <ldap:add config-ref="LDAP" doc:name="LDAP">
        </ldap:add>
        <object-to-string-transformer doc:name="Object to String"/>
 </flow>
The object-to-string transformer just makes sure the web service is returning a string representation of the mule message's payload.

Results

Below you can see the starting point. The SOAP message which will be sent to Mule ESB, and the Apache DS's situation.
And below you can see the response from the Mule ESB and the Apache DS's new entry.

Conclusions

While the ServiceNow's Orchestration is the ideal solution for this use case, there are always multiple ways to achieve the same results if you have time and expertise to invest =) Happy hacking.

No comments:

Post a Comment