AWS S3 Integration with ServiceNow
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-18-2020 06:18 AM
Could anyone please help with the details for AWS S3 bucket integration with ServiceNow ? I need to pull some badge data files into ServiceNow on a periodic basis. How could I establish this without using S3 Spoke as we do not have the license for Integration hub.
Thank you

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-18-2020 07:45 AM
For this to work, you will need to create a scope application to house one script include. There is no way around it. Just know it might impact your licensing. You will also assume all the risk - this is custom code and not related to SN in any way. SN will probably not provide support.
With that out of the way and you wish to continue following the steps below -
you will need to create a scoped app - I called mine key master - you will need to upload a CyptoJS library (which I attached to this post).
Now that you have that updated, you will need to create a Data Source to attached the file you are receiving from S3.
After that, you will need to create a scheduled import job. In that scheduled job drop the code below in the prescript section once you check the "execute pre-import script"
The script is where you set up the REST API to connect to S3.
You will have to add or modify different variables on the script as well. I left comments on which ones you need to change.
You will also have to modify the script to match the scoped app APIs. That this as an example of what you need to change.
"x_snc_key_master.CryptoJS.HmacSHA256(region, kDate); "
- anywhere where " x_snc_key_master.CryptoJS" is coded you will have to swap that out with the app name. It will be provided in the script include file in the API name filed (its in the picture above)
I also left logs in the script so you can troubleshoot along the way.
It is active and working in my personal instance.
// * Constructs the Authentication Header for Amazon AWS S3 Signature v4.
//https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
//@auther SERVICE-NOW.COM\MIGUEL.DONAYRE
//deleteAttachments();// this is optional - to delete old attachments to the data source record
// SETTING VARABILES
var accessID = '-------';// This come from AWS
var yourSecretAccessKeyID = '-------'; // This comes from AWS
var CanonicalResource = '/testfile.xlsx'; // REPLACE ME - The file name WITH file extension you want to retrive
var region = 'us-east-1'; //region of bucket
var service = 's3'; // service type
var uri = '/--BUCKET NAME__/testfile.xlsx'; // REPLACE ME/
var CanonicalURI = encodeURIComponent(uri);
var CanonicalQueryString = '';
var CanonicalHeaders = '';
var SignedHeaders = '';
var digest = new GlideDigest();
var HashedPayload = digest.getSHA256Hex(''); //ServiceNow's method for SHA256 hex
var hash = HashedPayload.toLowerCase();
var date = getDate();
//var jshash = x_snc_key_master.CryptoJS.SHA256(''); // using the crypto js way to SHA256 hex
//var hash = jshash.toLowerCase(); // uncomment out if you use the crytpo js method
//Headers
var header1 = 'host';
var headValue1 = 's3.amazonaws.com';
var header2 = 'x-amz-content-sha256';
var headValue2 = hash;
// Date converter for AWS date format
function getDate(){
var date;
{
var gdt = new GlideDateTime( );
var time = gdt.getTime( );
var t = time.getByFormat("HHmmss");
var wd = (function( ){
var map = {
'1' : 'Mon',
'2' : 'Tue',
'3' : 'Wed',
'4' : 'Thu',
'5' : 'Fri',
'6' : 'Sat',
'7' : 'Sun'
};
return map[gdt.getDayOfWeekUTC( ) + ''];
})();
var d = (function ( ) {
var map = {
'1' : '01',
'2' : '02',
'3' : '03',
'4' : '04',
'5' : '05',
'6' : '06',
'7' : '07',
'8' : '08',
'9' : '09',
'10' : '10',
'11' : '11',
'12' : '12',
'13' : '13',
'14' : '14',
'15' : '15',
'16' : '16',
'17' : '17',
'18' : '18',
'19' : '19',
'20' : '20',
'21' : '21',
'22' : '22',
'23' : '23',
'24' : '24',
'25' : '25',
'26' : '26',
'27' : '27',
'28' : '28',
'29' : '29',
'30' : '30',
'31' : '31'
};
return map[gdt.getDayOfMonthUTC( ) + ''];
})();
var m = (function ( ) {
var map = {
'1' : '01',
'2' : '02',
'3' : '03',
'4' : '04',
'5' : '05',
'6' : '06',
'7' : '07',
'8' : '08',
'9' : '09',
'10' : '10',
'11' : '11',
'12' : '12'
};
return map[gdt.getMonthUTC( ) + ''];
})();
var y = gdt.getYearUTC( );
date = y + m + d + 'T' + t + 'Z';
}
return date;
}
// gs.print('date = ' + date);
//Task 1: Create a Canonical Request
//<HTTPMethod>\n
//<CanonicalURI>\n
//<CanonicalQueryString>\n
//<CanonicalHeaders>\n
//<SignedHeaders>\n
//<HashedPayload>
// Creating the Canonical Header
CanonicalHeaders = header1.toLowerCase() + ':' + headValue1.trim() + '\n' +
header2.toLowerCase() + ':' + headValue2 ;
//gs.print ('CanonicalHeaders:' + CanonicalHeaders);
//Signed Headers
SignedHeaders = header1.toLowerCase() + ';' + header2.toLowerCase();
//Create CanonicalRequest
var CanonicalRequest = 'GET' + '\n' +
uri + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' + '\n' +
SignedHeaders + '\n' +
hash;
//gs.print('CanonicalRequest: '+ CanonicalRequest);
//Task 2: Create a String to Sign
//"AWS4-HMAC-SHA256" + "\n" +
//timeStampISO8601Format + "\n" +
//<Scope> + "\n" +
//Hex(SHA256Hash(<CanonicalRequest>))
// Setting Variables needed for String to Sign
var date_sub = date.substring(0,8);
var scope = date_sub + '/' + region + '/' + service + '/' + 'aws4_request';
var hashCanonical = digest.getSHA256Hex(CanonicalRequest);
var hashLower = hashCanonical.toLowerCase();
//var jshashCanonical = x_snc_key_master.CryptoJS.SHA256(CanonicalRequest);// using the crypto js way to hex
var stringToSign = 'AWS4-HMAC-SHA256' + '\n' +
date + '\n' +
scope + '\n' +
hashLower;
//gs.print('StringToSign: ' + stringToSign);
//Task 3: Calculate Signature
//DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
//DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
//DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
//SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
// Signature = HMAC-SHA256(StringtoSign, SigningKey)
var signature = getSig(yourSecretAccessKeyID, date_sub, region, service);
//gs.print('Signature: ' + signature);
//Create Authorization String
var authorization = 'AWS4-HMAC-SHA256 Credential=' + accessID + '/' + scope + ', SignedHeaders=' + SignedHeaders + ', Signature=' + signature;
//gs.print('authorization: ' + authorization);
getS3(authorization, date, headValue2);
// This creates the encoding of the Signature
function getSig(yourSecretAccessKeyID, date_sub, region, service){
try{
var kSecret = yourSecretAccessKeyID;
var kDate = x_snc_key_master.CryptoJS.HmacSHA256(date_sub, 'AWS4' + kSecret);
var kRegion = x_snc_key_master.CryptoJS.HmacSHA256(region, kDate);
var kService = x_snc_key_master.CryptoJS.HmacSHA256(service, kRegion);
var kSigning = x_snc_key_master.CryptoJS.HmacSHA256('aws4_request', kService);
var kSignature = x_snc_key_master.CryptoJS.HmacSHA256(stringToSign,kSigning);
//gs.print('kSignature: ' + kSignature);
return kSignature;
}
catch(e){
gs.log(e.string());
throw 'failed_missing_requirement';
}
}
function deleteAttachments(){
var gr = new GlideRecord('sys_attachment');
gr.addQuery('table_sys_id','e5dce20cdb3eb740148a94d9db961970');// Set variable to the record the attachment is being attached to
gr.query();
while(gr.next()){
gr.deleteRecord();
}
}
function getS3(auth, date, headValue2){
try{
var r = new sn_ws.RESTMessageV2();
r.setHttpMethod('get');
r.setEndpoint('https://s3.amazonaws.com/--BUCKET NAME---/testfile.xlsx');//REPLACE ME
r.setRequestHeader('Authorization', auth);
r.setRequestHeader('Date', date);
r.setRequestHeader('X-Amz-Content-Sha256', headValue2);
var tablename = 'sys_data_source';
var recordSysId = 'SYS ID of DATA SOURCE'; // REPLACE_ME
var filename = '13testfile.xlsx'; // REPLACE_ME with name you want to name the file
r.saveResponseBodyAsAttachment(tablename, recordSysId, filename);
var response = r.execute();
}
catch(ex){
var message = ex.message;
//gs.print('This is an Error');
gs.print(message);
}
//gs.print('This is the Body: ' + response.getBody());
//gs.print('Status: ' + response.getStatusCode());
}
All I did was take the steps the Amazon provides and broke it down in the script. If you want to see where I got is from here is the link to the Amazon's instructions
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
I can help out when but you will need to troubleshoot as much as you can.
I hope that helps!! Good luck!
Miguel
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎10-09-2020 01:08 AM
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
So I really doubt its something to do with the permissions for that AWS API user and I think we have passes the AWS authentication phase as we are not getting 401 error any more. What other things I should be checking at?
By the way, what we want to achieve is hitting AWS EC2/workspace APIs, but somehow we are unable to understand the values we should fill (for these services) in where your code says REPLACE_ME, so if you have any experience with that and help us with a sample file with some sample values in there, it would be a great help. Or maybe redirect to some other posts which can assist us with this. Obviously, we tried a lot and still trying to understand the AWS signature generation process from AWS docs, but being not an AWS expert and somewhat complex process of AWS signatures we are still unable to successfully hit the APIs through ServiceNow.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-21-2020 01:12 AM
Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎06-09-2022 09:36 AM
This post and the script provided by Miguel Donayre are valid.
1. Create an app with script include
2. Refer it in at other locations.
Below is sample of my script of flow action. Hope it helps someone wanting to try actions for Azure Service Bus with SAS authentication.
(function execute(inputs, outputs) {
const sharedKeyName = 'Your details'
const sharedKey = 'Your details'
const uri = 'Your details'
var encoded;
function createSharedAccessToken(uri, saName, saKey) {
if (!uri || !saName || !saKey) {
throw "Missing required parameter";
}
encoded = encodeURIComponent(uri);
gs.addInfoMessage(encoded);
var now = new Date();
var week = 60*60*24*7;
var ttl = Math.round(now.getTime() / 1000) + week;
var signature = encoded + '\n' + ttl;
const hash = x_hmac_encoding.CryptoJS.HmacSHA256(signature, saKey).toString(x_hmac_encoding.CryptoJS.enc.Base64);
return 'SharedAccessSignature sr=' + encoded + '&sig=' +
encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName;
}
// Set access token variable
outputs.access_token = createSharedAccessToken(uri, sharedKeyName, sharedKey);
//
var sm;
try{
sm = new sn_ws.RESTMessageV2();
sm.setHttpMethod("post");
// TODO: ChangeMe
sm.setEndpoint('https://xxxxx.servicebus.windows.net/automation-incoming/messages');
sm.setRequestHeader("Authorization", outputs.access_token);
sm.setRequestHeader("Content-Type", "application/json");
sm.setRequestBody({"msg":"First message from flow"});
//gs.addInfoMessage(sm.getRequestHeaders());
var response = sm.execute();
outputs.http_status = response.getStatusCode();
}catch(ex){
outputs.message = ex.getMessage();
}
})(inputs, outputs);