All skills
microsoft avatar

/azure-kusto-irql-graph

@4d0badd
by microsoftmicrosoft/skills3.1k stars
351

Apply IRQL graph functions to KQL or IRQL query results for Kusto Explorer visualization. Generates Lift_To_Graph mappings and composes Graph_Render_View, Graph_Fold_By_Property, Extract_Node_*, Enrich_Node_*, and Enrich_Graph_* calls. Accepts a supplied query or limited basic natural-language source request; it is not a general natural-language-to-KQL/IRQL skill. WHEN: Lift_To_Graph, Graph_Render_View, Graph_Fold_By_Property, IRQL graph enrichment, graph mapping for existing query results, icon-decorated graph, fold graph nodes. Use azure-kusto-graph for native make-graph analysis, graph-match, shortest paths, components, or persistent graphs.

Use this Skill: https://skilld.dev/gh/microsoft/skills/azure-kusto-irql-graph

This session only. Nothing lands on disk.

referencesDEPLOY_IRQL_FUNCTIONS.md

≈2.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Deploy IRQL Graph Functions

The Lift_To_Graph, Graph_Render_View, and Graph_Fold_By_Property functions must be present in the target Kusto database. They are pre-deployed on the kc7001 example cluster but may be missing on other clusters.

Check for existing functions

.show functions
| where Name in~ ("Lift_To_Graph", "Graph_Render_View", "Graph_Fold_By_Property")
| project Name

If any required function is missing, deploy it using the .create-or-alter commands below.

Lift_To_Graph

.create-or-alter function with (folder="irql_draft", docstring="Transforms a generic table to a Kusto graph table using the given JSON mapping")
Lift_To_Graph(T:(), mappingJson:string)
{
let calcIcon = (T:(type:string, defIcon:string)) {
    T
    | extend iconUrl = defIcon
    | project-away defIcon
};
let mapping = (mapping_json:string) {
    parse_json(mapping_json)
};
let Tpacked = (T:()) {
    T | extend _row = pack_all()
};
let KustoResultsToNodes = (T:(), mapping_json:dynamic) {
    let NodeExpanded =
        Tpacked(T)
        | mv-expand nodeDef = mapping(mapping_json).node_types to typeof(dynamic)
        | extend name=tostring(_row[tostring(nodeDef.key)]),
                 type=tostring(nodeDef.type),
                 _nodeKeys=iif(isnull(nodeDef.props),dynamic([]),nodeDef.props),
                 defaults=nodeDef.defaults
        | extend nodeColor=iff(isnotnull(nodeDef.color), tostring(_row[tostring(nodeDef.color)]), "")
        | extend nodeSize=iff(isnotnull(nodeDef.size), toreal(_row[tostring(nodeDef.size)]), 1.0)
        | extend iconColor=iff(isnotnull(nodeDef.iconColor), tostring(_row[tostring(nodeDef.iconColor)]), "")
        | extend id = strcat(nodeDef.id,"/",name)
        | extend nodeDisplayName=iff(isnotnull(nodeDef.displayName),
                    strcat(type,'/',tostring(_row[tostring(nodeDef.displayName)])), id)
        | extend defIcon = iif(isnotempty(nodeDef.defIcon), nodeDef.defIcon, "")
        | where isnotempty(split(id, "/")[-1])
        | extend type = tostring(nodeDef.type)
        | extend iconUrl=""
        | invoke calcIcon();
    let NodePropsFilled = (T:(_row:dynamic, _nodeKeys:dynamic, id:string, type:string,
            nodeDisplayName:string, nodeColor:string, nodeSize:real,
            iconUrl:string, iconColor:string, defaults:dynamic)) {
        T
        | mv-expand k=_nodeKeys to typeof(string)
        | extend v=_row[k], def=defaults[k]
        | extend v = iif(isnull(v) or isempty(tostring(v)), iif(isnull(def), v, def), v)
        | summarize properties=make_bag(bag_pack(k,v))
            by id,type,nodeDisplayName,nodeColor,nodeSize,iconUrl,iconColor
    };
    let NodeNoProps = (T:(_nodeKeys:dynamic, id:string, type:string,
            nodeDisplayName:string, nodeColor:string, nodeSize:real,
            iconUrl:string, iconColor:string)) {
        T
        | where array_length(_nodeKeys)==0
        | extend properties=dynamic({})
        | project id,type,properties,nodeDisplayName,nodeColor,nodeSize,iconUrl, iconColor
    };
    let Nodes = (T:(_row:dynamic, _nodeKeys:dynamic, id:string, type:string,
            nodeDisplayName:string, nodeColor:string, nodeSize:real,
            iconUrl:string, iconColor:string, defaults:dynamic))
    {
        union
            NodePropsFilled(T),
            NodeNoProps(T)
        | project id,type,properties,nodeDisplayName,nodeColor,nodeSize,iconUrl, iconColor
    };
    union
        (T | extend EntityType = "data"),
        (Nodes(NodeExpanded) | extend EntityType = "node")
};
let KustoResultsToEdges = (T:(EntityType:string, ),mapping_json:dynamic) {
    let edges = datatable(SourceId:string, TargetId:string) [];
    let EdgeExpanded =
        Tpacked((T | where EntityType == "data"))
        | extend nodeDef = mapping(mapping_json).node_types
        | mv-expand edgeDef = mapping(mapping_json).edges to typeof(dynamic)
        | mv-apply nodeDefSrc = nodeDef on (
            where tostring(nodeDefSrc["type"]) == tostring(edgeDef.source.type))
        | extend SourceId = strcat(nodeDefSrc.id,"/",tostring(_row[tostring(nodeDefSrc.key)]))
        | mv-apply nodeDefTgt = nodeDef on (
            where tostring(nodeDefTgt["type"]) == tostring(edgeDef.target.type))
        | extend TargetId = strcat(nodeDefTgt.id,"/",tostring(_row[tostring(nodeDefTgt.key)]))
        | extend edgeType=tostring(edgeDef.type),
                 _edgeKeys=iif(isnull(edgeDef.props),dynamic([]),edgeDef.props)
        | extend edgeDisplayName = iff(isnotnull(edgeDef.displayName),
                    strcat(edgeType,'/',tostring(_row[tostring(edgeDef.displayName)])), edgeType)
        | extend edgeColor= iff(isnotnull(edgeDef.color),
                    tostring(_row[tostring(edgeDef.color)]), edgeType);
    let EdgePropsFilled = (T:(_row:dynamic, _edgeKeys:dynamic,
            SourceId:string, TargetId:string, edgeType:string,
            edgeDisplayName:string, edgeColor:string)) {
        T
        | mv-expand k=_edgeKeys to typeof(string)
        | extend v=_row[k]
        | summarize edgeProperties=make_bag(bag_pack(k,v))
            by SourceId,TargetId,edgeType,edgeDisplayName,edgeColor
    };
    let EdgeNoProps = (T:(_edgeKeys:dynamic, SourceId:string, TargetId:string,
            edgeType:string, edgeDisplayName:string, edgeColor:string)) {
        T
        | where array_length(_edgeKeys)==0
        | extend edgeProperties=dynamic({})
        | project SourceId,TargetId,edgeType, edgeProperties,edgeDisplayName,edgeColor
    };
    let Edges = (T:(_edgeKeys:dynamic, SourceId:string, TargetId:string,
            edgeType:string, edgeDisplayName:string, edgeColor:string)) {
        union
            EdgePropsFilled(EdgeExpanded),
            EdgeNoProps(EdgeExpanded)
        | where isnotempty(split(SourceId, "/")[-1]) and isnotempty(split(TargetId, "/")[-1])
        | project-reorder SourceId,TargetId,edgeType,edgeProperties,edgeDisplayName,edgeColor
    };
    union
        (T | where EntityType=="node"),
        (Edges(EdgeExpanded) | extend EntityType = "edge")
};
T
| invoke KustoResultsToNodes(mappingJson)
| invoke KustoResultsToEdges(mappingJson)
| where EntityType != "data"
| project EntityType, id, type, properties, nodeDisplayName, nodeColor, nodeSize,
         iconUrl, iconColor, SourceId, TargetId, edgeType, edgeProperties,
         edgeDisplayName, edgeColor
}

Graph_Render_View

.create-or-alter function with (folder="irql_draft", docstring="Renders a graph table using make-graph in Kusto Explorer")
Graph_Render_View(T:(id:string, type:string, properties:dynamic, nodeDisplayName:string,
    nodeColor:string, nodeSize:real, iconUrl:string, iconColor:string,
    SourceId:string, TargetId:string, edgeType:string, edgeProperties:dynamic,
    edgeDisplayName:string, edgeColor:string, EntityType:string))
{
let NodesTable =
    T
    | where EntityType=="node"
    | project id, type, properties, nodeDisplayName, nodeColor, nodeSize, iconUrl, iconColor;
let EdgesTable =
    T
    | where EntityType=="edge"
    | project SourceId, TargetId, type=edgeType, properties=edgeProperties, edgeDisplayName, edgeColor;
// #graph-style("Default")
let Default = dynamic({
    "name":"Default",
    "graph_style":{
        "layout":{"kind":"Grouped"},
        "nodes_config":{
            "density":80.0,
            "label_by":"id",
            "color_by":"iconUrl",
            "lifetime_start_by":"",
            "lifetime_end_by":"",
            "image_url_by":"iconUrl",
            "image_size":2.0
        },
        "edges_config":{
            "lifetime_start_by":"",
            "lifetime_end_by":""
        }
    },
    "script":"// Use right-click on the nodes to explore interactive operations over the graph.",
    "matches":[]
});
EdgesTable
| make-graph SourceId --> TargetId with (NodesTable) on id
}

Graph_Fold_By_Property

.create-or-alter function with (folder="irql_draft", docstring="Folds nodes of a given type by a shared property value into single collapsed nodes")
Graph_Fold_By_Property(T:(EntityType:string, id:string, type:string, properties:dynamic,
    nodeDisplayName:string, nodeColor:string, nodeSize:real,
    iconUrl:string, iconColor:string,
    SourceId:string, TargetId:string, edgeType:string, edgeProperties:dynamic,
    edgeDisplayName:string, edgeColor:string), NodeType:string, PropertyName:string)
{
let Nodes =
    T
    | where EntityType == "node"
    | project EntityType, id, type, properties, nodeDisplayName, nodeColor, nodeSize, iconUrl, iconColor,
             SourceId="", TargetId="", edgeType="", edgeProperties=dynamic(null),
             edgeDisplayName="", edgeColor="";
let Edges =
    T
    | where EntityType == "edge"
    | project EntityType, id="", type="", properties=dynamic({}),
             nodeDisplayName="", nodeColor="", nodeSize=real(0), iconUrl="", iconColor="",
             SourceId, TargetId, edgeType, edgeProperties, edgeDisplayName, edgeColor;
let FoldedNodes =
    Nodes
    | where type == NodeType
    | where isnotempty(properties[PropertyName])
    | extend val = tostring(properties[PropertyName])
    | summarize members = make_list(id), memberCount = count() by val
    | where memberCount > 1
    | extend
        id = strcat(PropertyName, "/", val),
        type = PropertyName,
        EntityType = "node",
        properties = pack("folded", val,
                         "memberCount", memberCount,
                         "members", members),
        nodeDisplayName = strcat(PropertyName, "/", val),
        nodeColor = "", nodeSize = real(0),
        iconUrl = "", iconColor = ""
    | project EntityType, id, type, properties, nodeDisplayName, nodeColor, nodeSize, iconUrl, iconColor,
             SourceId="", TargetId="", edgeType="", edgeProperties=dynamic(null),
             edgeDisplayName="", edgeColor="";
let MemberToFold =
    Nodes
    | where type == NodeType
    | where isnotempty(properties[PropertyName])
    | extend val = tostring(properties[PropertyName])
    | join kind=inner (
        FoldedNodes
        | extend val = tostring(properties["folded"])
        | project val, foldId=id
      ) on val
    | project memberId=id, foldId;
let RewiredEdges =
    Edges
    | lookup kind=leftouter (MemberToFold | project SourceId=memberId, FoldSourceId=foldId) on SourceId
    | lookup kind=leftouter (MemberToFold | project TargetId=memberId, FoldTargetId=foldId) on TargetId
    | extend
        NewSourceId = coalesce(FoldSourceId, SourceId),
        NewTargetId = coalesce(FoldTargetId, TargetId)
    | where NewSourceId != NewTargetId
    | project EntityType="edge",
             id="", type="", properties=dynamic({}),
             nodeDisplayName="", nodeColor="", nodeSize=real(0), iconUrl="", iconColor="",
             SourceId=NewSourceId, TargetId=NewTargetId, edgeType, edgeProperties,
             edgeDisplayName, edgeColor;
let FoldedMemberIds = MemberToFold | distinct memberId;
union
    (Nodes | where id !in (FoldedMemberIds)),
    FoldedNodes,
    RewiredEdges
}

Deploying all three functions

Run each .create-or-alter block above in Kusto Explorer or the ADX web UI against your target database. The functions are placed in the irql_draft folder.

After deployment, verify:

.show functions
| where Name in~ ("Lift_To_Graph", "Graph_Render_View", "Graph_Fold_By_Property")
| project Name, Folder, DocString

Source: SKILL.md on GitHub

1 warning1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    This skill facilitates the transformation of Kusto Query Language (KQL) results into visual graphs within Kusto Explorer. It includes capabilities for generating KQL function deployment scripts and PowerShell scripts to automate the launch of the desktop application. These features include explicit security warnings and require user confirmation to ensure safe operation within the developer's environment.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: MEDIUM · 1 issue

Signed by skilld at 4d0badd. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 2 months ago
metadata
{
  "author": "Microsoft",
  "version": "1.2.1"
}

README badge

README badge for microsoft/skills/azure-kusto-irql-graph