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.
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
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.197America →
52.1.10.176,35.171.70.87
IPs for Azure and AWS Scans:
Europe →
18.158.77.90America →
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
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:
Provide tenant details
Begin by entering your integration name and the Azure tenant ID (available in your Azure portal).
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
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
Setting Up the Integration
Step 1: Add the integration in Cye Platform
Navigate to Settings > Integrations
Click Add Integration on the Microsoft Defender for Cloud tile:
Enter the Integration Name and Microsoft Azure Tenant ID:
In case of OAuth Authentication - Check the required steps needed and click Authenticate to Microsoft:
Select a Microsoft account (must be an Azure admin account):
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.
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:
In the Azure portal, search for Management groups and open it.
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.
Click Access control (IAM).
Click + Add > Add role assignment.
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.
On the Members tab, leave Assign access to on User, group, or service principal, then click + Select members.
Search for "CYE - Hyver Defender Cloud Prod", confirm its Application ID is [APP ID – to be added], select it, then click Select.
Click Review + assign, then Review + assign again.
Verify: on the Role assignments tab, the application appears with the Reader role.
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:
Auto-Fix Statuses
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
Viewing Results
Collected Endpoints
As part of the Defender for Cloud integration, Cye Platform collects data from the following API endpoints:
assessmentsThese 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-assessmentsThese provide the detailed results behind each assessment, showing which specific resources (like virtual machines or storage accounts) passed or failed the check.
regulatorycomplianceassessmentsThese 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
Editing and Deleting the Integration
Deleting the Integration
Click the Delete Integration button
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
Click the Edit Integration button
Make your changes
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
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
scope = "https://graph.microsoft.com/.default"
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
$graphUrl = "https://graph.microsoft.com/v1.0/applications?`$top=1"
$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
scope = "https://management.azure.com/.default"
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.
$graphUrl = "https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
$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)














