Skip to content

Instantly share code, notes, and snippets.

@gevaertw
Last active December 28, 2023 20:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gevaertw/5048676da755dccfabe1947050b24fc8 to your computer and use it in GitHub Desktop.
Save gevaertw/5048676da755dccfabe1947050b24fc8 to your computer and use it in GitHub Desktop.
#jArchi #AzureServiceMap
/*
---------------------------------------------------About------------------------------------------------------------------------------------------------------------------
Version 1
This script loads archi elements from an Azure service map into archimate. It Shows the network communication between servers as found by azure service map.
All will be drawn on an model but without considering the data already in the model. In V2 (if that ever gets created) I will work using existing models and the data it has
Use the query below in service map to get the input file. Do not change the fields or field order from the query, you can change the fileters if you like.
I highly recomend you to filter out any connection that you consider irrellevant or junk. Running an input file of 2000 lines takes around 90 seconds on my laptop.
for a full explaination of how to use this script refer to:
https://gevaertw.wordpress.com/2019/03/17/use-jarchi-to-draw-archi-relations-from-azure-service-map/
----------------------------------------------------Start of log analytics query------------------------------------------------------------------------------------------------------------------
VMConnection
| where Direction == "outbound"
| where DestinationIp != "127.0.0.1"
| distinct Computer, ProcessName, SourceIp, DestinationIp, Protocol, DestinationPort, RemoteDnsCanonicalNames, RemoteCountry)
----------------------------------------------------End of log analytics query------------------------------------------------------------------------
I have reused file opening code from
https://gist.github.com/smileham/1e57a5946235e780dee5a824f664aa3d
*/
//Global stuff
var fakeGUID = "00000000-AAAA-AAAA-AAAA-000000000000";
var archiPropertyCreatedBy = "Azure service map to Archi script" // everything creted by this script recieves this property, this helps filerering out in Archi
//----------------------------------------------------Object constructors---------------------------------------------------------------------------------------------------------------
function LogLine(computer_Parameter, processName_Parameter, sourceIP_Parameter, destinationIP_Parameter, protocol_Parameter, destinationPort_Parameter, remoteDnsCanonicalNames_Parameter, remoteCountry_Parameter, sourceComputerGUID_Parameter,remoteComputerGUID_Parameter)
{
this.lineSourceComputerName = computer_Parameter;
this.lineProcessName = processName_Parameter;
this.lineSourceIp = sourceIP_Parameter;
this.lineDestinationIp = destinationIP_Parameter;
this.lineProtocol = protocol_Parameter;
this.lineDestinationPort = destinationPort_Parameter;
this.lineRemoteComputerName = remoteDnsCanonicalNames_Parameter;
this.lineRemoteCountry = remoteCountry_Parameter;
this.lineSourceComputerGUID = sourceComputerGUID_Parameter;
this.lineRemoteComputerGUID = remoteComputerGUID_Parameter;
}
function ElementsLine(id_Parameter,name_Parameter,type_Parameter,elementIP_Parameter)
{
this.elementID = id_Parameter;
this.elementName = name_Parameter;
this.elementType = type_Parameter;
this.elementIP = elementIP_Parameter;
}
//----------------------------------------------------Functions used by the script-----------------------------------------------------------------------------------------------------
function isInElementsArray (inputElementArray_Parameter, elementName_Parameter)
{
//this function checks if a given element name is in the given elements array
a = 0;
elementFound = false;
//console.log("Array length: " + inputElementArrayP.length);
while (elementFound == false && a < inputElementArray_Parameter.length)
{
//console.log("Searching array in row: " + a);
if (inputElementArray_Parameter[a].elementName.toString() == elementName_Parameter)
{
elementFound = true;
//console.log("found element in row: " + a);
}
a++;
}
return elementFound;
}
//----------------------------------------------------Archi specific functions ---------------------------------------------------------------------------------------------------------
function archiElementObjectFromName(elementName_Parameter)
{
//This function accepts the name of an element and returns the coresponding ARCHI object.
searchString = ".".concat(elementName_Parameter);
archiCollection = $(searchString);
return archiCollection.first(); // we assume unique names in the model
}
function archiCreateElementOnModel (elementName_Parameter, elementType_Parameter,folder_Parameter)
{
//This function creates an element in Archi and returns the GUID
var d = new Date();
var element = model.createElement(elementType_Parameter,elementName_Parameter, folder_Parameter);
element.prop ("Created By", archiPropertyCreatedBy, false);
element.prop ("Created On", d.toUTCString(), false);
GUID = element.id;
return GUID;
}
function archiCreateRelationOnModel (realtionName_Parameter, sourceName_Parameter, targetName_Parameter, processName_Parameter,folder_Parameter)
{
//This function creates a relationship between 2 objects, it returns the GUID of the created relation
var d = new Date();
var realationType = "Flow-relationship"; // I use the lowest type of relation here.
sourceArchiObject = archiElementObjectFromName(sourceName_Parameter);
targetArchiObject = archiElementObjectFromName(targetName_Parameter);
var relationship = model.createRelationship(realationType, realtionName_Parameter , sourceArchiObject, targetArchiObject,folder_Parameter);
relationship.prop("Created By", archiPropertyCreatedBy, false);
relationship.prop("Created On", d.toUTCString(), false);
relationship.prop("Originating Process", processName_Parameter, false);
return relationship.id;
}
//----------------------------------------------------Functions that help debugging ---------------------------------------------------------------------------------------------------------
function showLineObjectArray (inputArray_Parameter)
{
//this function shows the array content of the file. I use it to help debugging
aLength = inputArray_Parameter.length;
console.log("the array has " + aLength + " lines");
for (arrayDisplayCount = 0; arrayDisplayCount< aLength; arrayDisplayCount++)
{
console.log(
arrayDisplayCount +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineSourceComputerName +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineProcessName +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineSourceIp +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineDestinationIp +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineProtocol +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineDestinationPort +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineRemoteComputerName +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineRemoteCountry +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineSourceComputerGUID +"|-|"+
inputArray_Parameter[arrayDisplayCount].lineRemoteComputerGUID
);
}
}
function showLineObjectArrayLine (inputArray_Parameter,line_Parameter)
{
//this function shows the array line of the file. I use it to help debugging
console.log(
"Line: "+ line_Parameter +"\n"+
"lineSourceComputerName: "+ inputArray_Parameter[LineP].lineSourceComputerName +"\n"+
"lineProcessName: "+ inputArray_Parameter[LineP].lineProcessName +"\n"+
"lineSourceIp: "+ inputArray_Parameter[LineP].lineSourceIp +"\n"+
"lineDestinationIp: "+ inputArray_Parameter[LineP].lineDestinationIp +"\n"+
"lineProtocol: "+ inputArray_Parameter[LineP].lineProtocol +"\n"+
"lineDestinationPort: "+ inputArray_Parameter[LineP].lineDestinationPort +"\n"+
"lineRemoteComputerName: "+ inputArray_Parameter[LineP].lineRemoteComputerName +"\n"+
"lineRemoteCountry: "+ inputArray_Parameter[LineP].lineRemoteCountry +"\n"+
"lineSourceComputerGUID: "+ inputArray_Parameter[LineP].lineSourceComputerGUID +"\n"+
"lineRemoteComputerGUID: "+ inputArray_Parameter[LineP].lineRemoteComputerGUID +"\n"
);
}
function showElementObjectArray (inputArray_Parameter)
{
//this function shows the array content of the Archi elements. I use it to help debugging
aLength = inputArray_Parameter.length;
console.log("the array has " + aLength + " lines");
for (arrayDisplayCount = 0; arrayDisplayCount< aLength; arrayDisplayCount++)
{
console.log(
arrayDisplayCount +"|-|"+
inputArray_Parameter[arrayDisplayCount].elementID +"|-|"+
inputArray_Parameter[arrayDisplayCount].elementName +"|-|"+
inputArray_Parameter[arrayDisplayCount].elementType +"|-|"+
//inputArrayP[arrayDisplayCount].elementDocumentation +"|-|"+
inputArray_Parameter[arrayDisplayCount].elementIP
);
}
}
function showElementObjectArrayLine (inputArray_Parameter,line_Parameter)
{
//this function shows the array line of Archi elements.. I use it to help debugging
console.log(
"Line: "+ line_Parameter +"\n"+
"elementID: "+ inputArray_Parameter[line_Parameter].elementID +"\n"+
"elementName: "+ inputArray_Parameter[line_Parameter].elementName +"\n"+
"elementType: "+ inputArray_Parameter[line_Parameter].elementType +"\n"+
//"elementDocumentation: "+ inputArrayP[lineP].elementDocumentation +"\n"+
"elementIP: "+ inputArray_Parameter[line_Parameter].elementIP +"\n"
);
}
function showTime ()
{
// this function shows the current time
var today = new Date();
var h = today.getHours();
var m = today.getMinutes();
var s = today.getSeconds();
return h +":"+m +":"+s;
}
//----------------------------------------------------start of script -----------------------------------------------------------------------------------------------------------------------------------------------
//First the file must be read
console.show();
console.clear();
console.log(showTime() + "> Import CSV");
var startTime = Date.now();
var filePath = window.promptOpenFile({ title: "Open CSV", filterExtensions: ["*.CSV"], fileName: "default.archimate" });
if (filePath)
{
var FileReader = Java.type("java.io.FileReader");
var theCSVFile = new FileReader(filePath);
var theCSV ="";
var data = theCSVFile.read();
while(data != -1) {
var theCharacter = String.fromCharCode(data);
theCSV+=theCharacter;
data = theCSVFile.read();
}
theCSVFile.close();
}
console.log(showTime() + "> File Loaded");
//cleanup file
var allLinesUnclean = theCSV.toString(); //the entire file as string
//remove all " [] form the string, it is junk
allLinesUnclean1 = allLinesUnclean.replace(/\"/g,"");
allLinesUnclean2 = allLinesUnclean1.replace(/\[/g,"");
allLines = allLinesUnclean2.replace(/\]/g,"");
var lines = allLines.split('\n'); //an array of all lines in the file as strings
var headerLine = lines[0].split(','); //the first line
var rowCount = lines.length; //The amount of lines parsed
var colCount = headerLine.length; //The amount of colums parsed
console.log (showTime() + ">There are " + rowCount + " lines in the file.");
//put the data, as objects in an array, row by row
var logAnalyticsArray = [];
var lineReader = new String(); //Will contain current row
var colSplit = new Array([colCount]); //Will contain all colls from row
var colReader = new String(); //Will contain current coll
console.log(showTime() + ">Start parsing");
for (rows = 0; rows < rowCount -1 ; rows++) // -1 to skip last line it is usualy the result of a \n\r at the end file somewhere
{
//lineReader = lines[rows];
//console.log("Working on line " + lineReader);
colSplit = lines[rows].split(',');
colComputer = colSplit[0];
colProcessName = colSplit[1];
colSourceIP = colSplit[2];
colDestinationIP = colSplit[3];
colProtocol = colSplit[4];
colDestinationPort = colSplit[5];
colRemoteDnsCanonicalNames = colSplit[6];
colRemoteCountry = colSplit[7];
colSourceComputerGUID = fakeGUID;
colRemoteComputerGUID = fakeGUID;
//last element (country) seems to contain newline characters, they must be removed or they create crappy info
colRemoteCountry = colRemoteCountry.replace(/\n/g,"");
colRemoteCountry = colRemoteCountry.replace(/\r/g,"");
//if the destination DNS could not be resolved it is null, in that case the put the destination IP there
if (colRemoteDnsCanonicalNames == null ||colRemoteDnsCanonicalNames == "")
{
colRemoteDnsCanonicalNames = colDestinationIP;
//console.log("Row with unknown hostname: "+ rows + " added: "+ colRemoteDnsCanonicalNames );
}
/*
else
{
console.log("Row has valid hostname: |" + colRemoteDnsCanonicalNames + "|");
}
*/
//if the destinatin country could not be resolved it is null, in that case we put unknown location
if (colRemoteCountry == null || colRemoteCountry == "")
{
//this does not seem to work well
colRemoteCountry = "Unknown location";
//console.log("Row with unknown country: "+ rows + " added "+ colRemoteCountry);
}
/*
else
{
console.log("Row has valid country: |"+ colRemoteCountry + "|");
}
*/
//Write the line object into the array
logAnalyticsArray[rows] = new LogLine (
colComputer,
colProcessName,
colSourceIP,
colDestinationIP,
colProtocol,
colDestinationPort,
colRemoteDnsCanonicalNames,
colRemoteCountry,
colSourceComputerGUID,
colRemoteComputerGUID
);
}
//Cleanup array of object
logAnalyticsArray.splice(0,1); //first 1 rows is blankline & header, we don't need it
//showLineObjectArray (logAnalyticsArray); //show content after parsing
console.log(showTime() + ">Done parsing");
/*
----------------------------------------------------Put Nodes on the model.----------------------
*/
var nodesToCreateOnModel = [];
// if the array is initaly empty we cannot compare its content later on so lets give it something. It is overwritten later on..
nodesToCreateOnModel.push (new ElementsLine (
"ID",
"Name",
"Node",
"IP Address"
));
//Create a folder to put the nodes in
var folder = $("folder.Technology & Physical").first();
var importTechSubFolder = folder.createFolder("Log Analytics Import");
//loop trough all lines in array
for (logAnalyticsComputerSearchCounter = 0; logAnalyticsComputerSearchCounter < logAnalyticsArray.length; logAnalyticsComputerSearchCounter++)
{
//console.log(logAnalyticsComputerSearchCounter);
sourceName = logAnalyticsArray[logAnalyticsComputerSearchCounter].lineSourceComputerName.toString();
remoteName = logAnalyticsArray[logAnalyticsComputerSearchCounter].lineRemoteComputerName.toString();
sourceIp = logAnalyticsArray[logAnalyticsComputerSearchCounter].lineSourceIp.toString();
remoteIP = logAnalyticsArray[logAnalyticsComputerSearchCounter].lineDestinationIp.toString();
sourceGUID = fakeGUID;
remoteGUID = fakeGUID;
/*
console.log(
logAnalyticsComputerSearchCounter + "|" +
"sourceName: "+ sourceName + ";" +
"remoteName: "+ remoteName +";" +
"sourceIp: "+ sourceIp +";" +
"remoteIP: "+ remoteIP +";" +
"sourceGUID: "+ sourceGUID +";" +
"remoteGUID: "+ remoteGUID+";"
);
*/
//If source name is not in the array add it, else skip it
if (isInElementsArray(nodesToCreateOnModel,sourceName) == false)
{
//sourcename not part of array, add it to the array
nodesToCreateOnModel.push (new ElementsLine (
fakeGUID,
sourceName,
"Node",
sourceIp
));
//immidiatlty create it on the Archi model, we get back the real GUID
sourceGUID = archiCreateElementOnModel(sourceName,"Node", importTechSubFolder);
//console.log("just created Source element: "+sourceGUID);
//Add GUID to nodes & Lines Array
nodesToCreateOnModel[nodesToCreateOnModel.length-1].elementID = sourceGUID;
logAnalyticsArray[logAnalyticsComputerSearchCounter].lineSourceComputerGUID = sourceGUID;
}
else
{
//it is part of the array so we only enrich the line with the source GUID
logAnalyticsArray[logAnalyticsComputerSearchCounter].lineSourceComputerGUID = archiElementObjectFromName(sourceName).id;
}
//If destination name is not in the array add it, else skip it
if (isInElementsArray(nodesToCreateOnModel,remoteName) == false)
{
//remoteName not part of array, add it to the array
nodesToCreateOnModel.push (new ElementsLine (
fakeGUID,
remoteName,
"Node",
remoteIP
));
//immidiatlty create it on the Archi model, we get back the real GUID
remoteGUID = archiCreateElementOnModel(remoteName,"Node",importTechSubFolder);
//console.log("just created Target element: "+ remoteGUID);
//Add GUID to nodes Array
nodesToCreateOnModel[nodesToCreateOnModel.length - 1].elementID = remoteGUID;
logAnalyticsArray[logAnalyticsComputerSearchCounter].lineRemoteComputerGUID = remoteGUID;
}
else
{
//it is part of the array so we only enrich the line with the remotecomputer GUID
logAnalyticsArray[logAnalyticsComputerSearchCounter].lineRemoteComputerGUID = archiElementObjectFromName(remoteName).id;
}
}
//showElementObjectArray (nodesToCreateOnModel);
//showLineObjectArray(logAnalyticsArray);
console.log(showTime() + ">New elements created");
/*
----------------------------------------------------put relations on the model----------------------
*/
//Create a folder to put the realations in
var folder = $("folder.Relations").first();
var importRelationsSubFolder = folder.createFolder("Log Analytics Import");
for (logAnalyticsRelationCreator = 0; logAnalyticsRelationCreator < logAnalyticsArray.length; logAnalyticsRelationCreator++)
{
sourceObjName = logAnalyticsArray[logAnalyticsRelationCreator].lineSourceComputerName.toString();
remoteObjName = logAnalyticsArray[logAnalyticsRelationCreator].lineRemoteComputerName.toString();
relationName = logAnalyticsArray[logAnalyticsRelationCreator].lineProtocol.concat(": ",logAnalyticsArray[logAnalyticsRelationCreator].lineDestinationPort);
processName = logAnalyticsArray[logAnalyticsRelationCreator].lineProcessName.toString();
archiCreateRelationOnModel (relationName,sourceObjName,remoteObjName,processName,importRelationsSubFolder);
}
console.log(showTime() + ">New relations Created");
/*
----------------------------------------------------Totaly done, if you see this happy face, nothing crashed :) ----------------------
*/
var endTime = Date.now();
runningTime = (endTime-startTime)/1000;
console.log(showTime() + ">Script done :) It took " + runningTime + " seconds to complete");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment