KQL :: return only tags with more than 4 records - kql

I have created a Kusto query that allows me to return all our database park. The query only takes 10 lines of code:
Resources
| join kind=inner (
resourcecontainers
| where type == 'microsoft.resources/subscriptions'
| project subscriptionId, subscriptionName = name)
on subscriptionId
| where subscriptionName in~ ('Subscription1','Subscription2')
| where type =~ 'microsoft.sql/servers/databases'
| where name != 'master'
| project subscriptionName, resourceGroup, name, type, location,sku.tier, properties.requestedServiceObjectiveName, tags.customerCode
By contract we are supposed to give only 4 Azure SQL Database per customer but sometimes developers take a copy of them and they rename it _old or _backup and suddenly a customer can have 5 or 6 databases.
This increase the overall costs of the Cloud and I would like to have a list of all customers that have more than 4 databases.
In order to do so I can use the tag tags.customerCode which has the 3 letters identifier for each customer.
The code should work like this: if a customer is called ABC and there are 4 Azure SQL Databases with tags.customerCode ABC the query should return nothing. If there are 5 or 6 databases with tags.customerCode ABC the query should return all of them.
Not sure if Kusto can be that flexible.

Here is a possible solution.
It should be noted that Azure resource graph supports only a limited subset of KQL.
resourcecontainers
| where type == 'microsoft.resources/subscriptions'
//and name in~ ('Subscription1','Subscription2')
| project subscriptionId, subscriptionName = name
| join kind=inner
(
resources
| where type =~ 'microsoft.sql/servers/databases'
and name != 'master'
)
on subscriptionId
| project subscriptionId, subscriptionName, resourceGroup, name, type, location
,tier = sku.tier
,requestedServiceObjectiveName = properties.requestedServiceObjectiveName
,customerCode = tostring(tags.customerCode)
| summarize dbs = count(), details = make_list(pack_all()) by customerCode
| where dbs > 4
| mv-expand with_itemindex=db_seq ['details']
| project customerCode
,dbs
,db_seq = db_seq + 1
,subscriptionId = details.subscriptionId
,subscriptionName = details.subscriptionName
,resourceGroup = details.resourceGroup
,name = details.name
,type = details.type
,location = details.location
,tier = details.tier
,requestedServiceObjectiveName = details.requestedServiceObjectiveName

Related

Self-join Kusto Query in Analytics Rule

I am working within Microsoft Sentinel Analytics Rules with the Kusto Query Language. (KQL)
I need to work in a Table called CrowdstrikeReplicatorLogs_CL which contains rows that contain a) data rows for which I need to alert on and b) metadata. that contains information about the subject in the alert.
This means I need to self-join the KQL table with itself to get the final result.
The column in question to join the table itself is the aid_g column.
ThreatIntelligenceIndicator
| where foo == bar
| join kind=innerunique (
CrowdstrikeReplicatorLogs_CL
| where TimeGenerated >= ago(dt_lookBack)
| where event_simpleName_s has_any ("NetworkConnectIP4", "NetworkConnectIP6")
| extend json=parse_json(custom_fields_message_s)
| extend ip4 = json["RemoteAddressIP4"], ip6=json["RemoteAddressIP6"]
| extend CS_ipEntity = tostring(iff(isnotempty(ip4), ip4, ip6))
| extend CommonSecurityLog_TimeGenerated = TimeGenerated
) on $left.TI_ipEntity == $right.CS_ipEntity
| join kind=innerunique (
CrowdstrikeReplicatorLogs_CL
| where custom_fields_message_s has "ComputerName"
| extend customFields=parse_json(custom_fields_message_s)
| project Hostname=customFields['ComputerName'], Platform=event_platform_s, aid_g
) on $left.aid_g == $right.aid_g
;
However, this raises a Query contains incompatible 'set' commands. error in Sentinel.
Is there a proper way to self-join tables?

Use LIKE with multiple names from a different table in UPDATE

I'm looking to update a field to a different value if the string in name contains a value from a different table in PostgreSQL:
Table Types that needs to be updated
id | name | type
1 | Google Page | WEBSITE
2 | Yahoo Page | WEBSITE
3 | Facebook Page | WEBSITE
...
Table Companies that has the names
id | name
1 | Google
2 | Yahoo
3 | Facebook
4 | Twitter
5 | Stackoverflow
...
What I tried
UPDATE types
SET type = 'BUSINESS'
WHERE name LIKE CONCAT((SELECT companies.name from companies), '% Page')
But I'm getting this issue: [21000] ERROR: more than one row returned by a subquery used as an expression
You could use a subquery with exists logic to retain your current logic:
UPDATE types t
SET type = 'BUSINESS'
WHERE EXISTS (SELECT 1 FROM companies c
WHERE CONCAT(c.name, ' Page') = t.name);
You could also use an update join:
UPDATE types t
SET type = 'BUSINESS'
FROM companies c
WHERE t.name = CONCAT(c.name, ' Page');
You should use the IN directive:
WHERE name IN (SELECT CONCAT(companies.name, ' Page') name from companies)
Much cheaper than comparing concatenated strings:
UPDATE types t
SET type = 'BUSINESS'
FROM companies c
WHERE right(t.name, 5) = ' Page'
AND left(t.name, -5) = c.name;
db<>fiddle here

Is there a way that I can have functionality like if-else in where condition

I have following table structure. On the basis what type of request comes, I should be able to pick the right column for matching the value.
| resource_id | result | request |
| | 15127 | /campaign/add |
| 15127 | | /campaign/modify |
For eg:
if the request is like /campaign/add then result should be compared with the number the is sent by the user else resource_id should be picked.
Programmatically I can say
if(request.equals())
fetch values from database where resource_id = 15127
else
fetch values from database where resource_id = 15127
Now I need to do same thing using database.
You can use a CASE expression to do that:
select *
from the_table
where case
when request = '/campaign/add' then result
when request = '/campaign/modify' then resource_id
end = number_sent_by_the_user
Is this what you want?
where (request like '/campaign/add%' and result = $user_Input) or
(request like '/campaign/modify%' and resource_id = $user_Input)

Filter SQL SELECT by XML node

There are single SQL Table with xml-type field:
| EntityId | EntityType | Xml column |
------------------------------------------------------
| 1 | Employee | `<productId>1</productId>`|
------------------------------------------------------
| 1 | Product | `<name>apple</name>` |
------------------------------------------------------
| 7 | Shop | `<country>...</country>` | |
-----------------------------------------------------|
What I need is a to filter table row by Xml node value:
SELECT * WHERE (EntityId='1' AND EntityType='Employee')
OR ( EntityId=SomeFuncToGetXmlFieldByNodeName('productId') )
Can u point me on how to write that SomeFuncToGetXmlFieldByNodeName(fieldName)
Looks like you want a function like this.
CREATE FUNCTION [dbo].[SomeFuncToGetXmlFieldByNodeName]
(
#NodeName nvarchar(100),
#XML xml
)
RETURNS nvarchar(max)
AS
BEGIN
RETURN #XML.value('(*[local-name(.) = sql:variable("#NodeName")]/text())[1]', 'nvarchar(max)')
END
It takes a node name and a some XML as parameter and returns the value in the node.
Use the function like this:
select T.EntityId,
T.EntityType,
T.[Xml column]
from YourTable as T
where T.EntityID = 1 and
T.EntityType = 'Employee' or
T.EntityId = dbo.SomeFuncToGetXmlFieldByNodeName('productId', T.[Xml column])
Instead of using the above I want to recommend you to try a query that does not use the scalar valued function. It uses exist() Method (xml Data Type) instead.
select T.EntityId,
T.EntityType,
T.[Xml column]
from YourTable as T
where T.EntityID = 1 and
T.EntityType = 'Employee' or
T.[Xml column].exist('/productId[. = sql:column("T.EntityID")]') = 1
I think you're looking for the documentation!
The "query" method is likely what you'll need. See examples in the linked article.

Query with conditions on multiple value column

I am building report in Oracle Apex 4.2. Table that report is build on has multiple values inside one of the columns.
-----------------------------------
| ID | NAME | PROJECT_ID |
-----------------------------------
| 1 | P1 | 23:45:56 |
| 2 | P2 | 23 |
| 3 | P3 | 45:65 |
-----------------------------------
I would like to build a query to retrieve names based on project_id's.
Select name from table where project_id = 23;
This obviously will return P2 only however I would like to build a query which would return P1 and P2 if we searched for 23.
Any help greatly appreciated.
You can use LIKE instead of = :
Select name from table where project_id LIKE '%23%';
If you've got a common delimiter such as the ':' in your example you could use the following to exclude results like '123':
SELECT name FROM table WHERE ':' || project_id || ':' LIKE '%:23:%'
By concatenating the delimiter to the front and back of the string, you don't have to write multiple criteria: LIKE '23:%' OR LIKE '%:23:%' OR LIKE '%:23' to handle the first and last number in the list.
This is a common design in Apex due to its builtin support for colon-delimited strings (e.g. to drive shuttle controls and other item types).
I generally use this pattern:
Select name from table where INSTR(':'||project_id||':',':23:') > 0;
P.S. It's a pity about that column name - I would have called it something like PROJECT_ID_LIST.