Skip to main content

Integrating with Microsoft Defender for Cloud – Full Guide

Step-by-step guide to integrating Microsoft Defender for Cloud with Cye Platform.

Introduction

According to Microsoft, Microsoft Defender for Cloud is a cloud-native application protection platform (CNAPP) that is made up of security measures and practices designed to protect cloud-based applications from various cyber threats and vulnerabilities.

Microsoft Defender logo

It offers:

  • Cloud Security Posture Management (CSPM)

  • Cloud Workload Protection (CWP)

  • DevOps security features

It is designed to safeguard multi-cloud and hybrid environments.


Integration Overview

This integration enables Cye Platform to ingest:

  • Host and cloud resource assets

  • Vulnerability data discovered by Microsoft Defender for Cloud

The ingested data is assessed in terms of:

  • Exposure

  • Maturity

  • Remediation priority

based on your business context.


Prerequisites

Required Role

You must be a Cye Platform administrator to add the integration.


Required Information

  • Azure Tenant ID


Required IP Addresses

For the integration to work smoothly, you may need to allow traffic from Cye Platform’s servers in your firewall or network configuration. This ensures that Cye Platform can securely connect to your environment and perform scans without being blocked.

Depending on your region and the type of scan, add the following IP addresses:

  • General IPs:

    • Europe → 18.198.79.197

    • America → 52.1.10.176, 35.171.70.87

  • IPs for Azure and AWS Scans:

    • Europe → 18.158.77.90

    • America → 34.206.252.13

In most cases, you only need to add the IPs relevant to your region and use case.


Cye Platform's Group Management and Integrations

If your organization includes multiple subsidiaries, Cye Platform supports a Group Management structure for viewing and managing risk across business units.

Where supported, integrations should be created at the subsidiary level, using credentials relevant to each subsidiary’s data.

In some integrations (such as Azure), certain findings exist only at the tenant level — in these cases, a dedicated “parent” company may be needed to connect to the root tenant. Findings stay within each subsidiary, while the parent company sees aggregated metrics.

For such a case we need to create Cye Platform companies per subsidiary + Cye Platform company for the global (tenant level) parent company for which all these companies are connected to.

For full details, see the Group Management guide.

Integration Process

Integrating Azure with Cye Platform follows a simple three-part flow. Understanding the framework upfront will make the step-by-step setup much easier:

  1. Provide tenant details

    Begin by entering your integration name and the Azure tenant ID (available in your Azure portal).

  2. Choose an authentication method:

    • OAuth Authentication (most recommended) - this is a tenant level integration that provides the Cye Platform instance access to the entire Azure tenant, allowing it to scan tenant level items such as users and access privileges related findings are only available through tenant level (OAuth) authentication.

    • Client ID and Secret Authentication - This type of authentication allows more flexibility and is more useful when you have different business units configured as subscriptions on your Azure tenant, and you want to manage findings for each business unit on a separate Cye Platform account. With this method, findings are limited to subscription level findings. Tenant level findings are excluded from the scan.

If you are hosting multi business units on the same Azure tenant, you might consider creating:

  • A cross business unit Cye Platform company that authenticates to the entire tenant through OAuth.

  • Company accounts per business unit, that authenticate through Client ID and Secret, mimicking your business structure using the Cye Platform Group Management setup.

For additional guidance on integrating Cye Platform with Azure, you may reach out to your TCSM or Cye Platform support.

Azure User Permissions Needed

The Azure user who sets up the integration needs:

  • Permissions to create an application

  • Permissions to create a service principal account

  • Permissions to assign the service principal account a "Reader" role to the relevant subscriptions

  • Microsoft Defender for Cloud must be enabled for your Azure subscriptions

Setting Up the Integration

Step 1: Add the integration in Cye Platform

  1. Navigate to Settings > Integrations

  2. Click Add Integration on the Microsoft Defender for Cloud tile:

  3. Enter the Integration Name and Microsoft Azure Tenant ID:

  4. In case of OAuth Authentication - Check the required steps needed and click Authenticate to Microsoft:

    1. Select a Microsoft account (must be an Azure admin account):

    2. Click "Accept" to grant the application access to the specified resources.

      The app name is defined by Cye Platform (for example, Cloud-Test), though it may vary.

      Note: The screenshot below is only an example of what the permissions screen looks like. In practice, you’ll see a longer list of permissions when setting up the integration.

    3. Upon successful authentication, you will see confirmation:


Step 2: Assign the Reader role in Azure

Complete the Authenticate and Accept steps above first. The Cye application only appears in your Azure tenant after you approve it.

Then give the Cye application read-only access to your Azure environment:

  1. In the Azure portal, search for Management groups and open it.
    ​


    ​

  2. Click the group at the top of the list, usually Tenant Root Group.

    If you don't have permissions on Tenant Root Group, open Subscriptions instead, select each subscription you want Cye to scan, and continue from the next step there.

  3. Click Access control (IAM).
    ​


    ​

  4. Click + Add > Add role assignment.
    ​

  5. On the Role tab, search for Reader and select the role named exactly Reader, described as "View all resources, but does not allow you to make any changes."

    Many other roles contain the word Reader (Backup Reader, Billing Reader, Key Vault Reader and more). None of those will work.

    Click Next.
    ​


    ​

  6. On the Members tab, leave Assign access to on User, group, or service principal, then click + Select members.
    ​


    ​

  7. Search for "CYE - Hyver Defender Cloud Prod", confirm its Application ID is [APP ID – to be added], select it, then click Select.
    ​


    ​

  8. Click Review + assign, then Review + assign again.
    ​

  9. Verify: on the Role assignments tab, the application appears with the Reader role.
    ​

  10. Wait 5–10 minutes for the role to take effect, then return to Cye Platform.


Step 3: Save the integration in Cye Platform

Once you’ve completed all the sub-steps shown on the Cye Platform integration page, click Save and wait for the confirmation message to appear:

  • Click Save to save the integration:

  • Verify that the integration is active:

  • Findings will appear in Cye Platform shortly

Auto-Fix Statuses

  • Auto-fix allows Cye Platform to automatically update the status of remediation assets (like servers or settings) when they are fixed or removed in the source system.

  • This means you don’t need to update the status manually — Cye Platform does it for you.

  • The status of each finding is determined based on its remediation assets.

Important: This integration does not support Auto-fix.

Viewing Results

Collected Endpoints

As part of the Defender for Cloud integration, Cye Platform collects data from the following API endpoints:

  • assessments

    These are high-level security evaluations run by Defender for Cloud. Each assessment checks whether a specific security control is properly configured (e.g., encryption, network exposure, etc.).

  • sub-assessments

    These provide the detailed results behind each assessment, showing which specific resources (like virtual machines or storage accounts) passed or failed the check.

  • regulatorycomplianceassessments

    These map your environment against recognized compliance frameworks such as NIST. They help identify gaps in compliance and highlight areas that need attention.

Editing and Deleting the Integration

Deleting the Integration

  1. Click the Delete Integration button

  2. Confirm deletion

When deleted:

  • Connection is immediately terminated

  • No new data is ingested or processed

  • Existing data remains in Cye Platform

When deleting the integration, please note: In addition to removing it from Cye Platform, you'll also need to delete it from the cloud side. To do this, go to the Enterprise Applications section in your cloud environment, search for "Cye", find the relevant Cye Platform entry, and delete it there as well.


Editing the Integration

  1. Click the Edit Integration button

  2. Make your changes

  3. Click Save

When editing the Defender for Cloud integration, please note that the only field you can change is the name.

Connecting via an Application in Your Own Tenant

Script
​
$ErrorActionPreference = "Stop"

function ValidateCreatedCredentials

{

param(

[string]$TenantId,

[string]$ClientId,

[string]$ClientSecret

)

Write-Host "Testing credentials with a Graph API call..."

try

{

# Get access token using client credentials flow

$body = @{

client_id = $ClientId

client_secret = $ClientSecret

grant_type = "client_credentials"

}

$tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"

$accessToken = $tokenResponse.access_token

# Call Microsoft Graph to get first app

$headers = @{

"Authorization" = "Bearer $accessToken"

"Content-Type" = "application/json"

}

$response = Invoke-RestMethod -Uri $graphUrl -Method Get -Headers $headers

if ($response.value.Count -gt 0)

{

Write-Host "Success: API test completed successfully Retrieved app: $( $response.value[0].displayName )"

return $true

}

else

{

Write-Host "Success: API test completed successfully, but no applications were found"

return $true

}

}

catch

{

Write-Host "ERROR: API test failed with: $_"

if ($_.Exception.Response)

{

Write-Host "Status: $( $_.Exception.Response.StatusCode.value__ ) - $( $_.Exception.Response.ReasonPhrase )"

}

return $false

}

}

function HandleExistingApps

{

param(

[string]$AppName,

[int]$Force

)

Write-Host "Checking for existing $AppName applications..."

$olderApps = az ad app list --filter "displayName eq '$AppName'" --query "[].{ObjectId:id, AppId:appId}" -o json | ConvertFrom-Json

$numOfOldApps = $olderApps.Count

$appObjectId = 0

$appId = 0

if ($numOfOldApps -gt 0)

{

if ($Force -eq 1)

{

Write-Host "Deletion might take few seconds"

foreach ($oldAppObjectId in $olderApps)

{

az ad app delete --id $oldAppObjectId.ObjectId

}

Write-Host "Deletion completed"

}

else

{

Write-Host "Using existing app $( $AppName ) found in the directory with Object ID($( $olderApps[0].ObjectId )) and App ID($( $olderApps[0].AppId ))"

Write-Host "To enforce new app creation and delete old $( $AppName ) apps, run the azure-onboarding script again with -force flag"

$appObjectId = $olderApps[0].ObjectId

$appId = $olderApps[0].AppId

}

}

else

{

Write-Host "No existing $AppName applications found or they were deleted"

}

return @{

AppObjectId = $appObjectId

AppId = $appId

}

}

function CreateNewApplication

{

param(

[string]$AppName

)

Write-Host "Configuring new $AppName application..."

$appReg = az ad app create --display-name "$AppName" --query "{ObjectId:id, AppId:appId}" -o json | ConvertFrom-Json

$appObjectId = $appReg.ObjectId

$appId = $appReg.AppId

Write-Host "$AppName application created. Object ID: $appObjectId, App ID: $appId"

# Update application homepage

Write-Host "Updating application data..."

az ad app update --id $appObjectId --web-home-page-url https://www.cyesec.com

return @{

AppObjectId = $appObjectId

AppId = $appId

}

}

function HandleServicePrincipal

{

param(

[string]$CurrentAppId

)

$AppServicePrincipal = az ad sp list --filter "appId eq '$CurrentAppId'" --query '[].{DisplayName:displayName, AppId:appId, Id:id}' -o json | ConvertFrom-Json

if ($AppServicePrincipal.Count -eq 0)

{

$spReg = az ad sp create --id $CurrentAppId --query "{Id:id}" -o json | ConvertFrom-Json

$spId = $spReg.Id

Write-Host "Service principal created. ID: $spId"

}

else

{

Write-Host "Service principal already exists. Display Name: $( $AppServicePrincipal.DisplayName ), App ID: $( $AppServicePrincipal.AppId ), Service Principal ID: $( $AppServicePrincipal.Id )"

$spId = $AppServicePrincipal.Id

}

return $spId

}

function TestGraphApiAccess

{

param(

[string]$TenantId,

[string]$ClientId,

[string]$ClientSecret,

[int]$MaxRetries = 25,

[int]$DelaySeconds = 5

)

Write-Host "Testing credentials..."

$accessToken = $null

$body = @{

client_id = $ClientId

client_secret = $ClientSecret

grant_type = "client_credentials"

}

for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {

try

{

$tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"

$accessToken = $tokenResponse.access_token

if ($accessToken)

{

Write-Host "Access token retrieved successfully"

break

}

Start-Sleep -Seconds $DelaySeconds

}

catch

{

Write-Host "Retrying token retrieval (attempt $attempt of $MaxRetries)"

Start-Sleep -Seconds $DelaySeconds

}

}

if (-not $accessToken)

{

Write-Host "ERROR: Failed to retrieve access token after $MaxRetries attempts"

throw "ERROR: Created credentials are invalid"

}

# Defender for Cloud is read through Azure Resource Graph 'securityresources', not Graph.

$headers = @{

"Authorization" = "Bearer $accessToken"

"Content-Type" = "application/json"

}

# First: what can this identity read at all? On a first run the Reader role has not been

# assigned yet - that is a later step in the guide, not a failure - so report it and stop

# rather than retrying, and never throw: the caller still has to print the credentials.

$visible = @()

try

{

$subs = Invoke-RestMethod -Uri "https://management.azure.com/subscriptions?api-version=2020-01-01" -Method Get -Headers $headers

$visible = @($subs.value | ForEach-Object { $_.subscriptionId })

}

catch

{

Write-Host "Could not list subscriptions with the new credentials: $_"

return

}

if ($visible.Count -eq 0)

{

Write-Host "No readable subscription yet - the Reader role has not been assigned."

Write-Host "Assign it as described in the guide, then run this script again to verify."

return

}

Write-Host "The application can read $( $visible.Count ) subscription(s)"

# Resource Graph keeps its own cache of role assignments and lags behind ARM by minutes, so

# a freshly assigned Reader returns 403 for a while. That is what this retry loop is for.

for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {

try

{

Write-Host "Testing credentials with a Resource Graph call (attempt $attempt of $MaxRetries)..."

# Scope the query to those subscriptions. A request naming neither subscriptions nor

# managementGroups runs at tenant scope, which Reader on a management group does not

# authorize, and comes back 403.

$argBody = @{

subscriptions = [string[]]$visible

query = "securityresources | where type =~ 'microsoft.security/assessments' | summarize assessments = count() by subscriptionId"

options = @{ resultFormat = "objectArray" }

} | ConvertTo-Json -Depth 5

$response = Invoke-RestMethod -Uri $graphUrl -Method Post -Headers $headers -Body $argBody

$rows = @($response.data)

Write-Host "API test successful. Readable subscriptions: $( $visible.Count ), assessments: $( ($rows | Measure-Object -Property assessments -Sum).Sum )"

return

}

catch

{

if ($_.Exception.Response)

{

Write-Host "Status: $( $_.Exception.Response.StatusCode.value__ ) - $( $_.Exception.Response.ReasonPhrase )"

}

Write-Host "Retrying API test (attempt $attempt of $MaxRetries)"

Start-Sleep -Seconds $DelaySeconds

}

}

# A failed verification must not destroy the run: the credentials are valid and the caller

# still needs to print them.

Write-Host "WARNING: The Resource Graph check did not succeed. Reader may still be replicating - verify from Cye Platform in a few minutes."

}

function CreateAndValidateApplicationCredentials

{

param(

[string]$CurrentAppObjectId,

[string]$TenantId,

[string]$CurrentAppId,

[int]$ManualConsent

)

$AppYears = (Get-Date).AddYears(50).ToString("yyyy-MM-ddTHH:mm:ssZ")

Write-Host "Creating client secret credential..."

$creds = az ad app credential reset --id $CurrentAppObjectId --end-date $AppYears --query "{Value:password}" -o json --only-show-errors | ConvertFrom-Json

$password = $creds.Value

# Test the credentials

if ($ManualConsent -eq 0)

{

TestGraphApiAccess -TenantId $TenantId -ClientId $CurrentAppId -ClientSecret $password

}

return $password

}

function ExtactArgs

{

param(

[string[]]$Params

)

$appName = "Cye-Defender-for-Cloud"

$force = 0

$skipCred = 0

$manualConsent = 0

if ($Params.Count -gt 0)

{

foreach ($arg in $Params)

{

if ($arg -eq "-force")

{

$force = 1

}

if ($arg -eq "-skipCred")

{

$skipCred = 1

}

if ($arg -like "-manualConsent")

{

$manualConsent = 1

}

if ($arg -like "-appName*")

{

$appName = $arg.Split("=")[1]

}

}

}

return @{

AppName = $appName

Force = $force

SkipCred = $skipCred

ManualConsent = $manualConsent

}

}

#START

$appSettings = ExtactArgs -Params $args

Write-Host "Onboarding script started for $( $appSettings.AppName )"

Write-Host "Fetching Tenant information..."

$tenantId = az account show --query "tenantId" -o tsv

$currentAppInfo = HandleExistingApps -AppName $appSettings.AppName -Force $appSettings.Force

if ($currentAppInfo.AppId -eq 0)

{

$currentAppInfo = CreateNewApplication -AppName $appSettings.AppName

}

$spId = HandleServicePrincipal -CurrentAppId $currentAppInfo.AppId

if ($appSettings.SkipCred -eq 0)

{

$password = CreateAndValidateApplicationCredentials -CurrentAppObjectId $currentAppInfo.AppObjectId -TenantId $tenantId -CurrentAppId $currentAppInfo.AppId -ManualConsent $appSettings.ManualConsent

}

Write-Host "Onboarding script finished successfully"

Write-Host ""

Write-Host "Copy the following parameters to $( $appSettings.AppName ) console"

Write-Host "tenant-id : $tenantId"

Write-Host "client-id : $( $currentAppInfo.AppId)"

Write-Host "client-secret : $password"
​

  • Instructions: (you can find the full script in the collapse)

Did this answer your question?