다음을 통해 공유


Power BI에 클라우드용 Defender 데이터 추가

클라우드용 Microsoft Defender의 데이터를 Microsoft Power BI와 연결하면 보안 메트릭을 쉽게 모니터링하고 분석할 수 있습니다. 통합을 통해 보안 인사이트를 시각화하고 잠재적인 위협과 취약성을 신속하게 식별할 수 있습니다. 이 문서에서는 복잡한 보안 정보를 명확하고 실행 가능한 인사이트로 변환하는 데 도움이 되는 Defender for Cloud 데이터를 Power BI에 연결하는 단계를 안내합니다.

필수 조건

Power BI를 Azure Resource Graph에 연결

클라우드용 Defender의 데이터를 Power BI에 연결하려면 먼저 Power BI를 Azure Resource Graph에 연결해야 합니다.

  1. 데스크톱에서 Power BI Desktop을 엽니다.

  2. 빈 보고서를 선택합니다.

  3. 데이터 가져오기>더 보기를 선택합니다.

    데이터 가져오기 단추의 위치와 추가 옵션을 보여주는 Power BI Desktop 기본 화면의 스크린샷입니다.

  4. Azure Resource Graph를 검색하여 선택합니다.

  5. 연결을 선택합니다.

클라우드 데이터용 쿼리 디펜더를 Power BI로 가져오기

Power BI Desktop이 Azure Resource Graph에 연결되면, Azure 리소스 그래프를 사용하여 클라우드용 Defender의 다양한 데이터 원본을 Power BI로 쿼리할 수 있습니다.

이 페이지에 제공된 쿼리는 결과를 제공하는 예시입니다. Azure Resource Graph를 사용하면 특정 요구 사항에 맞는 결과를 반환하도록 생성하고 사용자 지정할 수 있는 광범위한 데이터를 쿼리할 수 있습니다.

  1. 제공된 쿼리 중 하나를 복사하여 Power BI Desktop의 쿼리 편집기에 붙여 넣습니다.

    이 쿼리는 MDC의 위험별 보안 권장 사항을 검색하여 평가를 분석하고 주의가 필요한 영역을 식별할 수 있도록 합니다.

    securityresources 
            | where type =~ "microsoft.security/assessments"
            | extend assessmentType = iff(type == "microsoft.security/assessments", tostring(properties.metadata.assessmentType), dynamic(null))
            | where (type == "microsoft.security/assessments" and (assessmentType in~ ("BuiltIn", "CustomerManaged")))
            | extend assessmentTypeSkimmed = iff(type == "microsoft.security/assessments", case(
                        tostring(properties.metadata.assessmentType) == "BuiltIn", "BuiltIn",
                        tostring(properties.metadata.assessmentType) == "BuiltInPolicy", "BuiltIn",
                        tostring(properties.metadata.assessmentType) == "CustomPolicy", "Custom",
                        tostring(properties.metadata.assessmentType) == "CustomerManaged", "Custom",
                        tostring(properties.metadata.assessmentType) == "ManualCustomPolicy", "Custom",
                        tostring(properties.metadata.assessmentType) == "ManualBuiltInPolicy", "BuiltIn",
                        dynamic(null)
                    ), dynamic(null))
            | extend assessmentId = tolower(id)
            | extend assessmentKey = iff(type == "microsoft.security/assessments", name, dynamic(null))
            | extend source = iff(type == "microsoft.security/assessments", trim(' ', tolower(tostring(properties.resourceDetails.Source))), dynamic(null))
            | extend statusCode = iff(type == "microsoft.security/assessments", tostring(properties.status.code), dynamic(null))
            | extend resourceId = iff(type == "microsoft.security/assessments", trim(" ", tolower(tostring(case(source =~ "azure", properties.resourceDetails.Id,
                (type == "microsoft.security/assessments" and (source =~ "aws" and isnotempty(tostring(properties.resourceDetails.ConnectorId)))), properties.resourceDetails.Id,
                (type == "microsoft.security/assessments" and (source =~ "gcp" and isnotempty(tostring(properties.resourceDetails.ConnectorId)))), properties.resourceDetails.Id,
                source =~ "aws", properties.resourceDetails.AzureResourceId,
                source =~ "gcp", properties.resourceDetails.AzureResourceId,
                extract("^(?i)(.+)/providers/Microsoft.Security/assessments/.+$",1,id)
                )))), dynamic(null))
            | extend resourceName = iff(type == "microsoft.security/assessments", tostring(coalesce(properties.resourceDetails.ResourceName, properties.additionalData.CloudNativeResourceName, properties.additionalData.ResourceName, properties.additionalData.resourceName, split(resourceId, '/')[-1], extract(@"(.+)/(.+)", 2, resourceId))), dynamic(null))
            | extend resourceType = iff(type == "microsoft.security/assessments", tolower(properties.resourceDetails.ResourceType), dynamic(null))
            | extend riskLevelText = iff(type == "microsoft.security/assessments", tostring(properties.risk.level), dynamic(null))
            | extend riskLevel = iff(type == "microsoft.security/assessments", case(riskLevelText =~ "Critical", 4,
                      riskLevelText =~ "High", 3,
                      riskLevelText =~ "Medium", 2,
                      riskLevelText =~ "Low", 1,
                      0), dynamic(null))
            | extend riskFactors = iff(type == "microsoft.security/assessments", iff(isnull(properties.risk.riskFactors), dynamic([]), properties.risk.riskFactors), dynamic(null))
            | extend attackPaths = array_length(iff(type == "microsoft.security/assessments", iff(isnull(properties.risk.attackPathsReferences), dynamic([]), properties.risk.attackPathsReferences), dynamic(null)))           
            | extend displayName = iff(type == "microsoft.security/assessments", tostring(properties.displayName), dynamic(null))
            | extend statusCause = iff(type == "microsoft.security/assessments", tostring(properties.status.cause), dynamic(null))
            | extend isExempt = iff(type == "microsoft.security/assessments", iff(statusCause == "Exempt", tobool(1), tobool(0)), dynamic(null))
            | extend statusChangeDate = tostring(iff(type == "microsoft.security/assessments", todatetime(properties.status.statusChangeDate), dynamic(null)))
            | project assessmentId,
                        statusChangeDate,
                        isExempt,
                        riskLevel,
                        riskFactors,
                        attackPaths,
                        statusCode,
                        displayName,
                        resourceId,               
                        assessmentKey,
                        resourceType,
                        resourceName,
                        assessmentTypeSkimmed               
                | join kind=leftouter (
                    securityresources
                    | where type == 'microsoft.security/assessments/governanceassignments'
                    | extend assignedResourceId = tolower(iff(type == "microsoft.security/assessments/governanceassignments", tostring(properties.assignedResourceId), dynamic(null)))
                    | extend dueDate = iff(type == "microsoft.security/assessments/governanceassignments", todatetime(properties.remediationDueDate), dynamic(null))
                    | extend owner = iff(type == "microsoft.security/assessments/governanceassignments", iff(isempty(tostring(properties.owner)), "unspecified", tostring(properties.owner)), dynamic(null))
                    | extend governanceStatus = iff(type == "microsoft.security/assessments/governanceassignments", case(
                                isnull(todatetime(properties.remediationDueDate)), "NoDueDate",
                                todatetime(properties.remediationDueDate) >= bin(now(), 1d), "OnTime",
                                "Overdue"
                            ), dynamic(null))
                    | project assignedResourceId, dueDate, owner, governanceStatus
                ) on $left.assessmentId == $right.assignedResourceId
                | extend completionStatusNumber = case(governanceStatus == "Overdue", 5,
                                                           governanceStatus == "OnTime", 4,
                                                           statusCode == "Unhealthy", 3, 
                                                           isExempt, 7,
                                                           1)
                    | extend completionStatus = case(completionStatusNumber == 5, "Overdue",
                                                     completionStatusNumber == 4, "OnTime",
                                                     completionStatusNumber == 3, "Unassigned",
                                                     completionStatusNumber == 7, "Exempted",
                                                     "Completed")
                | where completionStatus in~ ("OnTime","Overdue","Unassigned")
                | project-away assignedResourceId, governanceStatus, isExempt
                           | order by riskLevel desc, attackPaths desc, displayName
    
  2. 확인을 선택합니다.

    Azure Resource Graph 쿼리를 입력할 위치와 확인 버튼의 위치를 보여주는 스크린샷입니다.

    참고 항목

    기본적으로 Resource Graph는 1000개의 레코드만 반환하도록 쿼리를 제한합니다. 이 제어는 큰 데이터 세트를 생성하는 의도하지 않은 쿼리로부터 사용자와 서비스를 둘 다 보호합니다. 쿼리 결과가 1000개 레코드 제한으로 잘리지 않도록 하려면 '고급 옵션 - $resultTruncated(선택 사항)'의 값을 FALSE로 설정합니다.

    고급 옵션의 위치와 거짓으로 설정하는 방법을 보여주는 스크린샷입니다.

  3. 로드를 선택합니다.

Azure Resource Graph를 사용하면 클라우드용 Defender 환경 내에서 사용 가능한 모든 데이터를 유연하게 검색하고 분석하여 포괄적이고 맞춤화된 인사이트를 확보할 수 있습니다. 데이터를 Power BI에 추가하면 비주얼리제이션과 대시보드를 만들어 보안 상태를 효과적으로 모니터링하고 관리할 수 있습니다.

다음 단계