Guide: Perl, Soap and Service-Now

Service-Now seems to be exploding in popularity of late and after working with the platform on and off over the last 6 months I can see why. Today I thought I might do a quick little tutorial on…

  1. How do I find the information needed to communicate with the SOAP API.

  2. How can you use Perl and the SOAP::Lite module to push and pull data from service-now.

Lets begin with the WSDL, no that’s not a typo, behind every good service-now page is a WSDL. The WSDL as it relates to service-now is basically an XML file filled with all of the SOAP functions available and what parameters those SOAP functions will take. The question now then is how do you get to one of these magical WSDL’s?

Well lets try looking at the Incident WSDL, on the left hand panel of your service-now right click on “Incidents” and open in a new window. The base of your URL should look something like: service-now.com/incident_list.do?really-long-string-of-arcane-fluff-that-means-nothing-to-us-sysadmins

Hmmmmm… lets mess with the URL a bit, change it to: service-now.com/incident_list.do?WSDL

Ah-ha! A big ol-slab of XML, if you minimize the xsd:elements under the xsd:schema element it will look like this:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified" targetNamespace="http://www.service-now.com/incident">
<xsd:element name="getKeys">...</xsd:element>
<xsd:element name="getKeysResponse">...</xsd:element>
<xsd:element name="get">...</xsd:element>
<xsd:element name="getResponse">...</xsd:element>
<xsd:element name="getRecords">...</xsd:element>
<xsd:element name="getRecordsResponse">...</xsd:element>
<xsd:element name="update">...</xsd:element>
<xsd:element name="updateResponse">...</xsd:element>
<xsd:element name="insert">...</xsd:element>
<xsd:element name="insertResponse">...</xsd:element>
<xsd:element name="deleteMultiple">...</xsd:element>
<xsd:element name="deleteMultipleResponse">...</xsd:element>
<xsd:element name="deleteRecord">...</xsd:element>
<xsd:element name="deleteRecordResponse">...</xsd:element>
</xsd:schema>

Get, update, insert, delete! Great now we have a list of commands and this same new window then query for the WSDL method will work on nearly every page in service-now! You can safely ignore the “Response” elements as we don’t need to know what’s in them, expand the getKeys element for now as that’s what our Perl example will be using.

Under xsd:sequence you will find a whole lot of elements, the two most important fields in these elements are name and type. The name will map to one of the fields you see in an actual incident and the type will tell us what data-type that field expects.

Now that we sort of have a grasp of what options are available to us lets do some Perl’ing.

Step 1. If necessary install SOAP::Lite

cpan SOAP::Lite

Step 2. Add a valid SOAP API user in service-now, in the left hand menu navigate to: System Web Services -> WS Security Profiles -> New and follow the prompts.

Step 3. Write some Perl! Below is an example script that will find the sys_id’s (unique identifiers) of all the incidents that have a category of “Network”.

#!/usr/bin/perl
use SOAP::Lite ( +trace => all, maptype => {} );

#If you don't have a proxy between you and service-now use this
#IMPORTANT: everything in <> brackets should be replaced with your own information... including the brackets
my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all');

#If you do have a proxy between you and service-now uncomment the next line instead
# my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all', proxy => ['https' => 'http://<proxy username>:<proxy password>@<proxyip>:<proxyport>']);

# Set up the method/functions we want to run.
my $method = SOAP::Data->name('getKeys') ->attr({xmlns => 'http://www.service-now.com/'});

# Set up the paramaters we want to run, in this case get all incidents with category Network
my @params = ( SOAP::Data->name(category => 'Network') );

# Execute the SOAP call.
my $result = $soap->call($method => @params)->result;

# Separate the results into an array of individual keys
my @sys_id_list = split(',',$result);

# Print the unique identifier for every ticket with the category of "Network"
foreach my $sys_id ( @sys_id_list ) {
       print ".... $sys_idn";
}

Cool, so we now have a printed list of completely useless strings. We’ll take this one step further then and try to get some actual information about the ticket:

#!/usr/bin/perl
use SOAP::Lite ( +trace => all, maptype => {} );

#If you don't have a proxy between you and service-now use this
#IMPORTANT: everything in <> brackets should be replaced with your own information... including the brackets
my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all');

#If you do have a proxy between you and service-now uncomment the next line instead
# my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all', proxy => ['https' => 'http://<proxy username>:<proxy password>@<proxyip>:<proxyport>']);

# Set up the method/functions we want to run.
my $method = SOAP::Data->name('getKeys') ->attr({xmlns => 'http://www.service-now.com/'});

# Set up the paramaters we want to run, in this case get all incidents with category Network
my @params = ( SOAP::Data->name(category => 'Network') );

# Execute the SOAP call.
my $result = $soap->call($method => @params)->result;

# Separate the results into an array of individual keys
my @sys_id_list = split(',',$result);

# Print the unique identifier for every ticket with the category of "Network"
foreach my $sys_id ( @sys_id_list ) {
       my $method = SOAP::Data->name('get') ->attr({xmlns => 'http://www.service-now.com/'});

        # Query for the ticket associated with the current sys_id
        my @params = ( SOAP::Data->name(sys_id => $sys_id) );

        # Service-Now will respond with a hash of all the key value pairs for that ticket.
        my %keyHash = %{ $soap->call($method => @params)->body->{'getResponse'} };

       print "sys_id: $sys_idtticket opened by: $keyHash{'opened_by'}tticket assigned to: $keyHash{'assigned_to'}n";
}

Now we’re starting to get somewhere. We’ve filtered out the tickets relevant to us (Networks), once we had the identifiers for those tickets we picked out the fields that interested us, in this case who opened the ticket and who it is assigned to. That’s pretty darn cool, but it leaves us with one last piece of the puzzle….

How do we create tickets?

#!/usr/bin/perl
use SOAP::Lite ( +trace => all, maptype => {} );

#If you don't have a proxy between you and service-now use this
#IMPORTANT: everything in <> brackets should be replaced with your own information... including the brackets
my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all');

#If you do have a proxy between you and service-now uncomment the next line instead
# my $soap = SOAP::Lite -> proxy('https://<servicenow WS user>:<password>@<myinstance>.service-now.com/incident.do?SOAP&displayvalue=all', proxy => ['https' => 'http://<proxy username>:<proxy password>@<proxyip>:<proxyport>']);

# Set up our insert method
my $soapInsert = SOAP::Data->name('insert') -> attr({xmlns => 'http://www.service-now.com/'});

# Build an array of all the fields to populate in the incident
my @soapInsertParams = (SOAP::Data->name(category => 'Networks'));
push(@soapInsertParams, SOAP::Data->name(subcategory => 'switches'));
push(@soapInsertParams, SOAP::Data->name(impact => 2));
push(@soapInsertParams, SOAP::Data->name(urgency => 2));
push(@soapInsertParams, SOAP::Data->name(priority => 2));
push(@soapInsertParams, SOAP::Data->name(assignment_group => 'Network Team'));
push(@soapInsertParams, SOAP::Data->name(short_description => 'A bad thing happened'));
push(@soapInsertParams, SOAP::Data->name(description => 'A bad thing happened with a switch'));

# Execute the insert!
my $soapReturn = $soap->call($soapInsert => @soapInsertParams);

# Catch any failures when inserting
if ($soapReturn->fault) {
      print $soapReturn->fault->{'faultcode'} . " " . $soapReturn->fault->{'faultstring'} . " " . $soapReturn->fault->{'detail'};
}

# Catch the sys_id of our newly created ticket, just in case we need to do more with it.
my %return_data = %{ $soapReturn->body->{'insertResponse'}};
my $sys_id = $return_data{'sys_id'};

And there we have it. Retrieving and inserting tickets into service-now is not as scary as it first seems!

comments powered by Disqus