Я хотел бы создать книгу Azure для отображения всех активаций PIM за последние x дней, и после того, как я сошел с ума и пролил много слез, теперь я застрял. Я не понимаю, как объединить событие запроса с событием утверждения. Насколько мне известно (или, скорее, насколько я пришел к выводу на основе данных в моей рабочей области Log Analytics), в процессе активации роли в PIM регистрируются 2 или 4 события:
1 и 4 регистрируются при каждой активации, а 2 + 3 регистрируются для утверждения. Пока все так просто.
Но как эти события соотносятся друг с другом, чтобы я мог автоматически отображать их с помощью KQL в пределах 1 строки?
Я не вижу никакого коррелирующего идентификатора (потому что, кстати, «CorrelationID» меняется между событием 2 и 3).
Я построил запрос KQL, который, вероятно, полностью переработан (потому что я понятия не имел о Kusto 3 дня назад, а мои знания SQL использовались 11 лет назад...)
Несколько слов о следующем коде: у меня была идея создать две временные таблицы Requests и Approvals и соединить их вместе - желательно через коррелирующий идентификатор, но я не могу его найти - через UserObjectID от запрашивающего пользователя в сочетании с RoleID и TimeGenerated (как можно ближе к запрашивающему событию). Но я понятия не имею, как это сделать. Мое видение результата — 1 активация в строке, а события без какого-либо необходимого одобрения имеют пустое поле в этом столбце, например:
let TimeSpan = 35d
let Request = (
AuditLogs
| where TimeGenerated > ago(TimeSpan)
| where OperationName == "Add member to role requested (PIM activation)"
| mv-apply AdditionalDetails on(
extend TicketNumber = iif (AdditionalDetails.key == "TicketNumber", tostring(AdditionalDetails.value), "")
| extend Justification = iif (AdditionalDetails.key == "Justification", tostring(AdditionalDetails.value), "")
| extend StartTime = iif (AdditionalDetails.key == "StartTime", tostring(AdditionalDetails.value), "")
| extend Expirationtime = iif (AdditionalDetails.key == "ExpirationTime", tostring(AdditionalDetails.value), "")
| extend IP = iif (AdditionalDetails.key== "ipaddr", tostring(AdditionalDetails.value), "")
)
| mv-apply tr = TargetResources on(
extend TargetUPN = TargetResources.userPrincipalName
| extend Permission = iff(tr.displayName == "Member", tostring(parse_json(TargetResources)[3].displayName), tostring(tr.displayName))
| extend RequestedRoleId = parse_json(TargetResources)["id"]
)
| mv-apply InitiatedBy on (
extend InitiatorUPN = InitiatedBy.user.userPrincipalName
| extend InitiatorDisplayName = InitiatedBy.user.displayName
| extend RequestorRoleId = InitiatedBy.user.id
)
| extend UserInternal = iff( InitiatorUPN contains "ext@","False","True")
| summarize take_any(TicketNumber)
,take_any(RequestorRoleId)
,take_any(Justification)
,take_any(StartTime)
,take_any(Expirationtime)
,take_any(IP)
,take_any(TargetUPN)
,take_any(InitiatorUPN)
,take_any(InitiatorDisplayName)
,take_any(UserInternal)
,take_any(Permission)
,take_any(RequestedRoleId) by TimeGenerated
);
let Approvals = (
AuditLogs
| where OperationName == "Add member to role request approved (PIM activation)"
| where TimeGenerated > ago(TimeSpan)
| mv-apply AdditionalDetails on(
extend ApproverJustification = iif (AdditionalDetails.key= = "Justification", tostring(AdditionalDetails.value), "")
| extend RequestorUserID = iif (AdditionalDetails.key= = "RequestId", tostring(AdditionalDetails.value), "")
)
| mv-apply InitiatedBy on(
extend ApproverDisplayName = parse_json(InitiatedBy)["user"]["displayName"]
| extend ApproverUPN = parse_json(InitiatedBy)["user"]["userPrincipalName"]
)
| mv-apply TargetResources on(
extend RequestedRoleId = parse_json(TargetResources)["id"]
)
| extend ApproverInternal = iff( InitiatorUPN contains "ext@","False","True")
| summarize take_any(RequestorUserID)
,take_any(RequestedRoleId)
,take_any(ApproverJustification)
,take_any(ApproverDisplayName)
,take_any(ApproverUPN)
,take_any(ApproverInternal)by TimeGenerated
);





Мне самому было трудно создать упрощенное представление, но я придумал следующее:
let daterange = 30d;
AuditLogs
| where LoggedByService == "PIM"
| where OperationName == "Add member to role completed (PIM activation)"
| where TimeGenerated > ago(daterange)
| project
TimeGenerated,
Reason = ResultReason,
Result,
Requestor = Identity,
Category,
Role = tostring(TargetResources[0].displayName),
ResourceName = tostring(TargetResources[3].displayName),
ResourceType = tostring(TargetResources[3].type),
Start = iif (Category == "GroupManagement", todatetime(AdditionalDetails[2].value), todatetime(AdditionalDetails[3].value)),
End = iif (Category == "GroupManagement", todatetime(AdditionalDetails[3].value), todatetime(AdditionalDetails[4].value)),
CorrelationId
| join kind=leftouter (
AuditLogs
| where TimeGenerated > ago(daterange)
| where LoggedByService == "PIM"
| where OperationName == "Add member to role request approved (PIM activation)"
| project CorrelationId, Approval = ResultDescription, Approver = Identity, Approved = Result)
on CorrelationId
| distinct TimeGenerated, Requestor, Reason, Approver, Approval, Category, Role, ResourceName, ResourceType, Start, End
Запрос и одобрение связаны столбцом CorrelationId. Это работает, если вы используете событие «Добавить участника к выполнению роли (активация PIM)» в качестве источника.
Единственное, чего не хватает, это номера билета, мы его не используем, но его тоже должно быть легко добавить.
большое спасибо, Питер :) Кажется, я просто двигаюсь в неправильном направлении, судя по различным деталям, зарегистрированным в различных событиях.
Добро пожаловать в stackoverflow. Очень сложно понять, как коррелируют ваши данные. И проверить ваш 30-строчный запрос, не имея реальных данных, тоже очень сложно. Я бы предложил создать пример меньшего размера, показывающий проблему, где вы знаете, как коррелируют ваши данные, и мы можем помочь вам с этим запросом.