Beginner ⏱ 75 min 📋 10 Steps

Set Up Cloud Discovery & Shadow IT Analysis

Configure Cloud Discovery in Microsoft Defender for Cloud Apps to gain full visibility into shadow IT usage across your organisation, analyse discovered applications and risk scores, deploy automated log collectors, integrate with Microsoft Defender for Endpoint, and establish ongoing governance.

📋 Overview

About This Lab

Microsoft Defender for Cloud Apps (MDA) is Microsoft's Cloud Access Security Broker (CASB) that provides visibility, data control, and threat protection across cloud services. Cloud Discovery analyses traffic logs from your firewalls and proxies to identify all cloud applications in use. sanctioned and unsanctioned. Without Cloud Discovery, organisations typically have no insight into the hundreds of cloud apps employees use outside of IT-approved channels. This lab walks you through setting up Cloud Discovery end-to-end: from snapshot reports to automated continuous discovery with Docker log collectors and MDE integration.

🏢 Enterprise Use Case

Contoso Corp, a mid-size company with 2,000 employees, discovers during an internal audit that employees are using over 400 unsanctioned cloud applications. The security team has no visibility into what cloud services employees are uploading sensitive data to, creating significant compliance and data leakage risks.

Management has mandated a 30-day project to establish full cloud app visibility, assess risk, and implement a governance framework. The IT team must classify every discovered app as Sanctioned, Unsanctioned, or Under Review, and block high-risk unsanctioned apps. Success criteria: continuous discovery enabled, all apps categorised, high-risk apps blocked, and an executive report delivered to leadership.

🎯 What You Will Learn

  1. Navigate the Cloud Apps portal and understand Cloud Discovery architecture
  2. Prepare and upload firewall/proxy logs to create snapshot reports
  3. Analyse discovered applications, risk scores, and usage patterns
  4. Understand and customise risk score criteria for your organisation
  5. Configure continuous reports with Docker-based log collectors
  6. Integrate with Microsoft Defender for Endpoint for automatic discovery
  7. Create app tags (Sanctioned/Unsanctioned/Under Review) and custom categories
  8. Set up Cloud Discovery anomaly detection policies
  9. Block unsanctioned apps via MDE integration
  10. Generate executive reports and establish ongoing governance

🔑 Why This Matters

The average enterprise uses over 1,000 cloud apps, but IT is typically aware of fewer than 10%. the rest is shadow IT. Shadow IT creates uncontrolled data exfiltration paths: employees may store sensitive corporate data in unapproved cloud storage, AI tools, or collaboration platforms.

Regulatory frameworks (GDPR, HIPAA, SOC 2) require organisations to know where their data resides and who can access it. Cloud Discovery provides the foundation for all CASB capabilities: you cannot govern what you cannot see. Integrating with MDE enables agent-based discovery that captures traffic even when users are off the corporate network.

⚙️ Prerequisites

  • Licensing: Microsoft Defender for Cloud Apps (standalone) or Microsoft 365 E5 / E5 Security / E5 Compliance
  • Portal Access: Global Administrator, Security Administrator, or Cloud App Security Administrator role in security.microsoft.com
  • Firewall/Proxy Logs: Access to exported log files from your firewall or proxy appliance (Palo Alto, Fortinet, Zscaler, Squid, etc.)
  • Docker Host (for continuous reports): A Linux VM (Ubuntu 18.04+) or Windows Server 2019+ with Docker installed
  • MDE Integration: Microsoft Defender for Endpoint Plan 2 with devices onboarded (for automatic discovery)
  • Network: Outbound HTTPS (443) connectivity from the log collector host to *.us.portal.cloudappsecurity.com (or your regional endpoint)
  • PowerShell: PowerShell 7+ recommended for running API scripts, with the Az module installed
  • API Token: A Defender for Cloud Apps API token (generated in Settings > Cloud Apps > API tokens) for script-based queries
⚠️ Important: Log formats vary between firewall vendors. Verify your appliance's log format is supported by checking the supported firewalls and proxies list before starting.

Step 1 · Navigate the Cloud Apps Portal & Understand Cloud Discovery Architecture

Cloud Discovery in Microsoft Defender for Cloud Apps works by analysing traffic logs to identify and score every cloud application your organisation uses. Understanding the architecture ensures you choose the right data source strategy.

Cloud Discovery Data Sources

  • Snapshot Reports: One-time manual upload of firewall/proxy logs for an initial point-in-time view
  • Continuous Reports (Log Collector): Automated log ingestion via a Docker-based syslog collector that continuously streams logs to the cloud
  • MDE Integration: Agent-based discovery from Windows 10/11 endpoints onboarded to Microsoft Defender for Endpoint · no log uploads needed
  • Secure Web Gateway Integration: Native integration with Zscaler, iboss, Corrata, and Menlo Security for inline discovery

Navigate to Cloud Discovery

  1. Sign in to the Microsoft Defender portal at security.microsoft.com
  2. In the left navigation, expand Cloud Apps and select Cloud Discovery
  3. Review the Dashboard tab · this is your primary view for discovered apps, categories, and risk data
  4. Explore the tabs: Discovered apps, Discovered resources, IP addresses, and Users
  5. Note the Create snapshot report button in the top action bar · you will use this in the next step
# ---------------------------------------------------------------
# PURPOSE: Verify connectivity to the Defender for Cloud Apps API.
# WHY: Before running any automation, confirm your API token is valid
#      and the MDA endpoint is reachable. A 401 means an invalid token;
#      a timeout means a firewall or DNS issue.
# PREREQUISITES: Generate an API token in Settings > Cloud Apps > API tokens.
#      Replace YOUR_API_TOKEN_HERE with your actual token.
# OUTPUT: On success → "Connected" + total discovered app count.
#         On failure → the HTTP error message (401, 403, timeout, etc.).
# ---------------------------------------------------------------
$mdaUrl = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/discovery/"
$token  = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"   # MDA uses token-based auth (not Bearer)
    "Content-Type"  = "application/json"
}

# Send a simple GET to the discovered_apps endpoint as a connectivity check.
# TimeoutSec 15 avoids hanging if outbound 443 is blocked.
try {
    $response = Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/" `
        -Headers $headers -Method GET -TimeoutSec 15
    Write-Host "Connected to MDA API. Discovered apps count: $($response.total)" -ForegroundColor Green
} catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
}
Pro Tip: Bookmark the Cloud Discovery dashboard (Cloud Apps > Cloud Discovery) and visit it weekly. The dashboard updates daily with new data and is your single pane of glass for shadow IT visibility.

Step 2 · Prepare and Upload Firewall/Proxy Logs (Snapshot Report)

A snapshot report gives you an immediate point-in-time view of cloud app usage. Export logs from your firewall or proxy, then upload them to MDA for analysis.

Prepare Your Log Files

  1. Log in to your firewall/proxy management console (e.g., Palo Alto Panorama, Fortinet FortiGate, Zscaler Admin Portal)
  2. Export traffic logs covering the last 7·30 days · include all outbound HTTP/HTTPS traffic
  3. Ensure the export includes: timestamp, source IP, destination URL/domain, bytes sent/received, and action (allow/deny)
  4. Save the log file in the vendor's native format (CSV, TSV, syslog, W3C, or CEF) · MDA supports 25+ formats
  5. If logs exceed 5 GB, split them into smaller files · the portal supports multi-file uploads per report

Create a Snapshot Report

  1. In the Defender portal, go to Cloud Apps > Cloud Discovery
  2. Click Actions > Create Cloud Discovery snapshot report
  3. Enter a name for the report, e.g., Contoso-Audit-Jan2026
  4. Select your Data source · choose your firewall/proxy vendor from the dropdown
  5. Upload your log file(s) and click Create
  6. Wait for processing to complete (typically 5·15 minutes depending on file size)
  7. Once ready, the snapshot appears in the Cloud Discovery dashboard under the report selector dropdown
# ---------------------------------------------------------------
# PURPOSE: Export outbound traffic logs from a Palo Alto firewall
#          for upload to MDA Cloud Discovery as a snapshot report.
# WHY: MDA needs raw traffic logs to identify which cloud apps
#      your users are accessing. Palo Alto logs contain source IP,
#      destination URL, bytes, and action - exactly what MDA parses.
# ---------------------------------------------------------------

# OPTION A - Palo Alto CLI (SSH session):
# > set cli pager off          ← disables paged output
# > show log traffic direction equal 1 receive_time in last-30-days
# direction=1 means outbound traffic only. Save the output as CSV.

# OPTION B - Palo Alto XML API (programmatic export):
# Replace YOUR_API_KEY with the key from Device > API Keys on the firewall.
$paloAltoHost = "https://pa-firewall.contoso.com"
$apiKey = "YOUR_API_KEY"

# Query filter: outbound traffic (direction eq 1) from Jan 1 2026 onward.
# Adjust the date range to capture 7–30 days of traffic for a meaningful snapshot.
$logQuery = "( direction eq 1 ) and ( receive_time geq '2026/01/01 00:00:00' )"

# Call the Palo Alto REST API to retrieve matching traffic log entries.
$uri = "$paloAltoHost/api/?type=log&log-type=traffic&query=$logQuery&key=$apiKey"
$logData = Invoke-RestMethod -Uri $uri -Method GET

# Save to disk in UTF-8. MDA accepts CSV, TSV, syslog, W3C, and CEF formats.
$logData | Out-File -FilePath "C:\Logs\paloalto-traffic-export.csv" -Encoding UTF8

# OUTPUT: File size in MB - if >5 GB, split before uploading to the MDA portal.
Write-Host "Log file exported. Size: $((Get-Item 'C:\Logs\paloalto-traffic-export.csv').Length / 1MB) MB"
Pro Tip: Always export at least 7 days of logs for a meaningful snapshot. A single day's data may miss apps used intermittently. For your initial audit, 30 days of data provides the most comprehensive view of shadow IT.

Step 3 · Analyse Discovered Applications and Risk Scores

After your snapshot processes, MDA identifies all cloud apps and assigns each one a risk score from 1 (highest risk) to 10 (lowest risk) based on 90+ risk factors.

Review the Cloud Discovery Dashboard

  1. Navigate to Cloud Apps > Cloud Discovery and select your snapshot report from the dropdown
  2. Review the Dashboard tab: it shows total discovered apps, categories (storage, collaboration, CRM, etc.), and usage trends
  3. Click Discovered apps to see the full app catalogue with risk scores, users, traffic volume, and IP addresses
  4. Sort by Risk score (ascending) to surface the riskiest applications first
  5. Click on any app name to open its detailed profile: compliance certifications, security features, legal information, and headquarters location

Understanding Risk Score Categories

  • General (25%): Company information, domain registration date, popularity, headquarters country
  • Security (35%): Encryption at rest, multi-factor authentication, audit trail, data classification, vulnerability management
  • Compliance (15%): SOC 2, ISO 27001, HIPAA, GDPR, PCI DSS certifications
  • Legal (25%): Data ownership, data retention policy, DMCA compliance, user audit trail, privacy policy
# ---------------------------------------------------------------
# PURPOSE: Retrieve discovered apps from MDA sorted by risk score
#          (ascending) so the riskiest apps appear first.
# WHY: After a snapshot or continuous report processes, you need to
#      identify which shadow IT apps pose the greatest risk. Sorting
#      by ascending risk (1=highest risk, 10=lowest) surfaces the
#      most dangerous apps at the top for immediate triage.
# OUTPUT: A table of the 20 riskiest apps showing name, risk score,
#         category (e.g. Cloud Storage, CRM), user count, and
#         traffic volume in MB. High user counts with low risk scores
#         are your top priority for remediation.
# ---------------------------------------------------------------
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# POST request with sort parameters. limit=50 fetches the top 50;
# change to 500+ for a full catalogue export.
$body = @{
    filters = @{}                # No filter = all apps
    sortField = "risk"           # Sort by risk score
    sortDirection = "asc"        # Ascending: riskiest (score 1) first
    limit = 50
} | ConvertTo-Json -Depth 5

$apps = Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/" `
    -Headers $headers -Method POST -Body $body

# Format the top 20 into a readable table.
# Traffic is converted from bytes to megabytes for readability.
$apps.data | Select-Object -First 20 | ForEach-Object {
    [PSCustomObject]@{
        AppName   = $_.name        # Display name of the cloud app
        RiskScore = $_.risk        # 1 (highest risk) to 10 (lowest risk)
        Category  = $_.category    # Cloud Storage, Collaboration, CRM, etc.
        Users     = $_.users       # Number of unique users accessing this app
        Traffic   = [math]::Round($_.traffic / 1MB, 2)  # Data volume in MB
    }
} | Format-Table -AutoSize

Write-Host "`nTotal Discovered Apps: $($apps.total)" -ForegroundColor Cyan
Pro Tip: Focus your initial review on apps with a risk score of 1·4 that also have high user counts or traffic volume. These represent the highest-impact shadow IT risks · a risky app used by 5 people is less urgent than a risky app used by 500.

Step 4 · Understand and Customise Risk Score Criteria

Default risk scores may not reflect your organisation's specific priorities. Customise the scoring weights to align with your regulatory requirements and risk appetite.

Customise Score Metrics

  1. Navigate to Settings > Cloud Apps > Cloud Discovery > Score metrics
  2. Review the four scoring categories: General, Security, Compliance, and Legal
  3. Adjust the importance slider for each category (1·10). For a healthcare organisation, increase Compliance weight; for a financial institution, increase Security weight
  4. Within each category, toggle individual risk factors on/off. For example, if GDPR compliance is mandatory, set it to required
  5. Click Save · risk scores will recalculate across all discovered apps within minutes

Recommended Customisation for Contoso

  • Security: Increase to importance 10 · prioritise encryption, MFA, and audit trail requirements
  • Compliance: Increase to importance 8 · SOC 2 and GDPR are mandatory for Contoso's data processing
  • Legal: Keep at importance 5 · data ownership and privacy policies are important but secondary
  • General: Decrease to importance 3 · popularity and company age are less relevant for risk assessment
# ---------------------------------------------------------------
# PURPOSE: Export all discovered apps to CSV after adjusting score
#          metrics, so you can compare before/after risk scores and
#          share with stakeholders for governance decisions.
# WHY: When you change scoring weights (e.g. increase Security to 10),
#      risk scores recalculate. Exporting to CSV lets you track
#      score changes, share with compliance/legal, and document
#      which apps became higher/lower risk after customisation.
# OUTPUT: CSV file at C:\Reports\ with columns: AppName, RiskScore,
#         Category, Users, TrafficMB, ComplianceRisk, SecurityRisk.
#         Open in Excel to sort/filter by any column.
# ---------------------------------------------------------------
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# Fetch up to 500 apps sorted by risk. Increase limit for larger environments.
$body = @{
    filters = @{}
    sortField = "risk"
    sortDirection = "asc"
    limit = 500
} | ConvertTo-Json -Depth 5

$apps = Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/" `
    -Headers $headers -Method POST -Body $body

# Build a flat object for each app and export to CSV.
# ComplianceRisk/SecurityRisk show the sub-score breakdown so
# stakeholders can see exactly why an app's overall score changed.
$apps.data | ForEach-Object {
    [PSCustomObject]@{
        AppName        = $_.name             # Cloud app display name
        RiskScore      = $_.risk             # Overall risk score (1–10)
        Category       = $_.category         # E.g. Cloud Storage, CRM
        Users          = $_.users            # Unique users accessing the app
        TrafficMB      = [math]::Round($_.traffic / 1MB, 2)  # Traffic in MB
        ComplianceRisk = $_.complianceRisk   # Compliance sub-score
        SecurityRisk   = $_.securityRisk     # Security sub-score
    }
} | Export-Csv -Path "C:\Reports\MDA-DiscoveredApps-Scored.csv" -NoTypeInformation

Write-Host "Exported $($apps.total) apps to C:\Reports\MDA-DiscoveredApps-Scored.csv" -ForegroundColor Green
Pro Tip: Document your score metric customisations and the rationale behind them. When auditors ask why a specific app was classified as high-risk, you need to demonstrate that the scoring criteria align to your documented risk framework.

Step 5 · Configure Continuous Reports with Log Collectors

Snapshot reports provide a one-time view, but continuous reports give you real-time, ongoing visibility. Deploy a Docker-based log collector that receives syslog feeds from your firewall and automatically uploads them to MDA.

Create a Data Source in the Portal

  1. Go to Settings > Cloud Apps > Cloud Discovery > Automatic log upload
  2. Click Add data source
  3. Enter a name (e.g., Contoso-PaloAlto-HQ), select the Source (Palo Alto, Fortinet, etc.), and Receiver type (Syslog. UDP/TCP or FTP)
  4. Click Add · the portal generates a unique data source configuration
  5. Next, click Add log collector to create the collector that receives this data source
  6. Name the collector (e.g., Contoso-LogCollector-01), assign the data source you just created, and note the docker run command displayed

Deploy the Docker Log Collector

  1. Provision a Linux VM (Ubuntu 20.04+ recommended) with at least 2 CPU cores, 4 GB RAM, and 100 GB disk
  2. Install Docker Engine on the host
  3. Run the Docker command provided by the portal to deploy the log collector container
  4. Configure your firewall to send syslog to the collector's IP address on the designated port
  5. Verify the collector is receiving logs by checking its status in the portal
# ---------------------------------------------------------------
# PURPOSE: Deploy the MDA Docker-based log collector that receives
#          syslog from your firewall and uploads logs to the cloud.
# WHY: Snapshot reports are one-time; a log collector provides
#      continuous, automated Cloud Discovery - new apps are detected
#      within hours of first use, not weeks later.
# PREREQUISITES:
#   - Linux VM (Ubuntu 20.04+), 2 CPU, 4 GB RAM, 100 GB disk
#   - Docker Engine installed
#   - Outbound HTTPS (443) to *.portal.cloudappsecurity.com
#   - Data source & log collector created in the MDA portal
# ---------------------------------------------------------------

# Step 1: Install Docker on the Linux VM (if not already installed)
# sudo apt-get update
# sudo apt-get install -y docker.io
# sudo systemctl enable docker --now

# Step 2: Run the MDA Log Collector container.
# Replace PUBLICIP, CONSOLE, COLLECTOR, and TOKEN with values
# from Settings > Cloud Apps > Automatic log upload.
$dockerCommand = @"
docker run -d --name mda-log-collector \
  --restart always \
  -p 514:514/udp \
  -p 514:514/tcp \
  -p 21:21/tcp \
  -p 20000-20099:20000-20099/tcp \
  -e "PUBLICIP=10.0.1.50" \
  -e "PROXY=" \
  -e "CONSOLE=contoso.us3.portal.cloudappsecurity.com" \
  -e "COLLECTOR=ContosoLogCollector01" \
  -e "TOKEN=YOUR_COLLECTOR_TOKEN_HERE" \
  mcr.microsoft.com/mcas/logcollector:latest
"@
# Flags explained:
#   -d              → run in detached (background) mode
#   --restart always → auto-restart on crash or host reboot
#   -p 514:514/udp  → receive syslog over UDP (most firewalls)
#   -p 514:514/tcp  → receive syslog over TCP (for reliable delivery)
#   -p 21 + 20000+  → FTP ports for firewalls that send logs via FTP
#   PUBLICIP        → the IP your firewall sends syslog to
#   CONSOLE         → your MDA regional endpoint URL
#   COLLECTOR       → the collector name you defined in the portal
#   TOKEN           → authentication token from the portal

Write-Host $dockerCommand -ForegroundColor Cyan
Write-Host "`n# Copy and run this command on your Docker host" -ForegroundColor Yellow

# Step 3: Verify the container is running and check logs for errors.
# docker ps --filter "name=mda-log-collector"
# docker logs mda-log-collector --tail 50
# Look for "Connected to console" in the output.

# Step 4: Test syslog reception - send a dummy syslog message locally.
# If the collector is working, this line appears in docker logs.
# echo "<134>Jan 01 00:00:00 test-fw traffic: allow src=10.0.0.5 dst=example.com" | nc -u 127.0.0.1 514

Verify Collector Health

# ---------------------------------------------------------------
# PURPOSE: Check the health of all deployed log collectors via API.
# WHY: A disconnected collector means a gap in Cloud Discovery data.
#      Monitor status regularly to catch issues before they create
#      blind spots (e.g. expired certificates, disk full, network change).
# OUTPUT: Table showing each collector's Name, Status (Connected/
#         Disconnected), LastUpdated timestamp, and assigned DataSources.
#         Status = "Connected" + recent LastUpdated = healthy.
# ---------------------------------------------------------------
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# GET request to the log_collectors endpoint returns all registered collectors.
$collectors = Invoke-RestMethod -Uri "$($mdaUrl)discovery/log_collectors/" `
    -Headers $headers -Method GET

# Format the response into a readable status table.
$collectors.data | ForEach-Object {
    [PSCustomObject]@{
        Name        = $_.name            # Collector display name
        Status      = $_.status          # Connected / Disconnected / Warning
        LastUpdated = $_.lastModified     # Last time logs were received
        DataSources = ($_.dataSources | ForEach-Object { $_.name }) -join ", "
    }
} | Format-Table -AutoSize
Pro Tip: Deploy at least two log collectors for redundancy · configure your firewall to send syslog to both. If one collector goes offline, you maintain continuous visibility with no gaps in your discovery data.

Step 6 · Integrate with Microsoft Defender for Endpoint for Automatic Discovery

The most powerful Cloud Discovery data source is MDE integration. Once enabled, every Windows 10/11 endpoint onboarded to MDE automatically reports cloud app usage · even when users are working remotely, off the corporate network.

Enable MDE Integration

  1. Navigate to Settings > Endpoints > Advanced features
  2. Locate Microsoft Defender for Cloud Apps and toggle it to On
  3. Also ensure Custom network indicators is set to On (required for app blocking in Step 9)
  4. Click Save preferences
  5. Wait 2·4 hours for MDE to begin forwarding cloud app telemetry to MDA
  6. Navigate to Cloud Apps > Cloud Discovery and select the built-in Win10 Endpoint Users report to view MDE-sourced data

Verify the Integration

# ---------------------------------------------------------------
# PURPOSE: Verify that MDE → MDA integration is enabled and that
#          endpoints are reporting cloud app data to Cloud Discovery.
# WHY: MDE integration is the most powerful discovery source - it
#      captures cloud app usage from every onboarded endpoint, even
#      when users are off-network (home Wi-Fi, VPN, cellular).
#      This script checks both the integration toggle and the custom
#      network indicators feature (required for blocking in Step 9).
# OUTPUT: Green checkmarks for enabled features; red/yellow for
#         disabled features that need attention. Also shows the
#         count of MDE endpoints actively reporting to Cloud Discovery.
# ---------------------------------------------------------------

# Use an MDE API token (Bearer auth, not Token auth like MDA).
# Generate at security.microsoft.com > Settings > APIs > API tokens.
$mdeToken = "YOUR_MDE_API_TOKEN"
$headers = @{
    "Authorization" = "Bearer $mdeToken"
    "Content-Type"  = "application/json"
}

# Check if MDA integration feature is enabled
try {
    $features = Invoke-RestMethod -Uri "https://api.securitycenter.microsoft.com/api/preferences" `
        -Headers $headers -Method GET
    
    $mdaFeature = $features | Where-Object { $_.featureName -eq "cloudAppsIntegration" }
    if ($mdaFeature.isEnabled) {
        Write-Host "? MDE → MDA integration is ENABLED" -ForegroundColor Green
    } else {
        Write-Host "? MDE → MDA integration is DISABLED" -ForegroundColor Red
    }
    
    $networkIndicators = $features | Where-Object { $_.featureName -eq "customNetworkIndicators" }
    if ($networkIndicators.isEnabled) {
        Write-Host "? Custom network indicators are ENABLED (required for app blocking)" -ForegroundColor Green
    } else {
        Write-Host "? Custom network indicators are DISABLED · enable for app blocking" -ForegroundColor Yellow
    }
} catch {
    Write-Host "Error querying MDE API: $($_.Exception.Message)" -ForegroundColor Red
}

# Check how many MDE-onboarded devices are reporting cloud app data
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$mdaToken = "YOUR_MDA_API_TOKEN"
$mdaHeaders = @{
    "Authorization" = "Token $mdaToken"
    "Content-Type"  = "application/json"
}

$devices = Invoke-RestMethod -Uri "$($mdaUrl)discovery/discovered_apps/?source=mde" `
    -Headers $mdaHeaders -Method GET

Write-Host "`nMDE endpoints reporting to Cloud Discovery: $($devices.total)" -ForegroundColor Cyan
Pro Tip: MDE integration discovers cloud apps even when employees are on personal Wi-Fi or VPN. This is critical for organisations with remote/hybrid workforces where firewall logs only capture on-network traffic.

Step 7 · Create App Tags and Custom Categories

Tags let you classify discovered apps by governance status. Every app should be marked as Sanctioned, Unsanctioned, or Under Review. Custom tags let you add organisation-specific labels for deeper categorisation.

Built-in Tag Options

  • Sanctioned: Apps approved by IT. These are your official, supported cloud services (e.g., Microsoft 365, Salesforce, ServiceNow)
  • Unsanctioned: Apps that violate policy or pose unacceptable risk. These can be blocked at the network/endpoint level
  • Under Review: Apps being evaluated by the security team. Pending a decision on sanction or block status

Tag Apps in the Portal

  1. Go to Cloud Apps > Cloud Discovery > Discovered apps
  2. Select one or more apps using the checkboxes
  3. Click Tag as sanctioned, Tag as unsanctioned, or Tag as under review from the action bar
  4. For bulk operations, use the Filter to narrow apps by risk score (e.g., score = 3) and tag all filtered results as Unsanctioned
  5. For individual apps, click the app name, then use the tag dropdown in the app detail page

Create Custom App Tags

  1. Go to Settings > Cloud Apps > App tags
  2. Click Add app tag
  3. Create tags that align with your governance model, for example:
    • PII-Risk · apps that may process personally identifiable information
    • Finance-Approved · apps approved specifically by the Finance department
    • Pending-Legal-Review · apps requiring legal team review for data residency or DPA
    • Migration-Target · unsanctioned apps whose users should migrate to a sanctioned alternative
# ---------------------------------------------------------------
# PURPOSE: Bulk-tag all discovered apps with risk score 1–3 as
#          "Unsanctioned" using the MDA API.
# WHY: Apps scoring 1–3 have significant security, compliance, or
#      legal deficiencies (e.g. no encryption, no SOC 2, poor privacy
#      policy). Tagging them as Unsanctioned prepares them for
#      endpoint-level blocking via MDE (Step 9) and marks them
#      in the portal as unapproved for your organisation.
# OUTPUT: Per-app confirmation of tagging, plus a total count.
#         If MDE blocking is enabled, unsanctioned app URLs will be
#         pushed as network indicators within ~2 hours.
# ---------------------------------------------------------------
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# Filter: risk score between 1 (highest risk) and 3.
# Adjust the range to match your organisation's risk threshold.
$body = @{
    filters = @{
        risk = @{ gte = 1; lte = 3 }
    }
    limit = 200
} | ConvertTo-Json -Depth 5

$riskyApps = Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/" `
    -Headers $headers -Method POST -Body $body

Write-Host "Found $($riskyApps.total) high-risk apps (score 1-3)" -ForegroundColor Yellow

# Loop through each app and apply the "unsanctioned" tag via the tag endpoint.
foreach ($app in $riskyApps.data) {
    $tagBody = @{ appId = $app.appId; tag = "unsanctioned" } | ConvertTo-Json
    try {
        Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/tag/" `
            -Headers $headers -Method POST -Body $tagBody
        Write-Host "  Tagged '$($app.name)' (Score: $($app.risk)) as Unsanctioned" -ForegroundColor Red
    } catch {
        Write-Host "  Failed to tag '$($app.name)': $($_.Exception.Message)" -ForegroundColor DarkRed
    }
}

Write-Host "`nBulk tagging complete." -ForegroundColor Green
Pro Tip: Start with a conservative approach · tag uncertain apps as "Under Review" rather than immediately blocking them. Notify the users of those apps first to avoid business disruption, then move to "Unsanctioned" after the review period.

Step 8 · Set Up Cloud Discovery Anomaly Detection Policies

Discovery policies alert you when new apps appear, when usage patterns change, or when anomalous behaviour is detected · so you catch new shadow IT before it becomes entrenched.

Built-in Discovery Policies

  • New app discovered: Triggers when a previously unseen cloud app appears in your discovery data
  • New high-volume app: Alerts on new apps with unusually high traffic or user counts
  • New risky app: Fires when a newly discovered app has a risk score below a configured threshold
  • Anomalous behaviour: Detects sudden spikes in usage for existing apps (e.g., data uploads tripling overnight)

Create a Custom Discovery Policy

  1. Navigate to Cloud Apps > Policies > Policy management
  2. Click Create policy > App discovery policy
  3. Name the policy: Alert on New High-Risk Apps
  4. Set the filter: Risk score <= 4
  5. Configure the alert: send email notifications to the security team distribution list
  6. Enable Daily alert digest to avoid alert fatigue
  7. Under Governance actions, optionally set the app to be automatically tagged as "Under Review"
  8. Click Create

Create an Anomaly Detection Policy

  1. Click Create policy > Cloud Discovery anomaly detection policy
  2. Name: Anomalous Upload to Cloud Storage
  3. Set the trigger: Uploaded data anomaly on apps in category Cloud storage
  4. Set severity to High and enable email alerts
  5. Click Create
# ---------------------------------------------------------------
# PURPOSE: List all Cloud Discovery policies currently configured
#          in your MDA tenant via the API.
# WHY: Auditing your discovery policies ensures you have coverage
#      for new apps, risky apps, and anomalous usage patterns.
#      This helps verify that the policies created in Step 8 are
#      active and correctly configured.
# OUTPUT: Table of policies showing name, severity level, enabled
#         status, and creation date. Disabled policies appear but
#         are not actively triggering alerts.
# ---------------------------------------------------------------
$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# Filter by policyType = 5, which corresponds to Cloud Discovery policies.
# Other policy types: 0=Activity, 1=File, 2=OAuth app, etc.
$body = @{
    filters = @{
        policyType = @{ eq = 5 }  # 5 = Discovery policy type
    }
} | ConvertTo-Json -Depth 5

$policies = Invoke-RestMethod -Uri "$($mdaUrl)policies/" `
    -Headers $headers -Method POST -Body $body

$policies.data | ForEach-Object {
    [PSCustomObject]@{
        PolicyName = $_.name       # Policy display name
        Severity   = $_.severity   # Low, Medium, High
        Enabled    = $_.enabled    # True = actively monitoring
        Created    = $_.created    # Creation timestamp
    }
} | Format-Table -AutoSize

Write-Host "Total Discovery policies: $($policies.total)" -ForegroundColor Cyan
Pro Tip: Use daily alert digests instead of individual alerts for discovery policies. Shadow IT changes slowly · you do not need real-time alerting for new app discoveries. A daily summary keeps your security team informed without creating noise.

Step 9 · Block Unsanctioned Apps via MDE Integration

Once you have tagged apps as Unsanctioned, you can enforce blocking directly on endpoints via MDE. This prevents users from accessing blocked apps regardless of their network location · no firewall or proxy changes needed.

How MDE App Blocking Works

  • When an app is tagged as Unsanctioned in MDA, its associated URLs and domains are pushed to MDE as custom network indicators
  • MDE's network protection engine blocks access to those indicators on every onboarded Windows 10/11 endpoint
  • Users who attempt to access a blocked app see a browser warning page explaining the app is blocked by their organisation
  • Blocking works across all browsers and network connections (corporate, home Wi-Fi, cellular)

Enable App Blocking

  1. Ensure the MDE integration is enabled (Step 6) and Custom network indicators is turned on
  2. Navigate to Settings > Cloud Apps > Cloud Discovery > Microsoft Defender for Endpoint
  3. Toggle Block unsanctioned apps to On
  4. Click Save
  5. Any app currently tagged as Unsanctioned will be blocked on MDE-onboarded endpoints within 2 hours
  6. To verify, try accessing an unsanctioned app from an MDE-managed endpoint · you should see the block page

Test and Monitor Blocking

# ---------------------------------------------------------------
# PURPOSE: Confirm that MDA-unsanctioned app URLs have been pushed
#          to MDE as custom network indicators (block rules).
# WHY: When you tag an app as "Unsanctioned" and enable MDE blocking,
#      MDA automatically creates URL indicators in MDE. This script
#      verifies those indicators exist and tests endpoint blocking.
# OUTPUT: Table of blocked URLs with action (Block/Allow), severity,
#         and alert settings. The test section confirms end-to-end
#         blocking by attempting to reach a blocked domain.
# ---------------------------------------------------------------
$mdeToken = "YOUR_MDE_API_TOKEN"
$headers = @{
    "Authorization" = "Bearer $mdeToken"
    "Content-Type"  = "application/json"
}

# Query the MDE indicators API, filtering for indicators created
# by the CloudAppSecurity source (i.e. MDA integration).
$indicators = Invoke-RestMethod `
    -Uri "https://api.securitycenter.microsoft.com/api/indicators?`$filter=source eq 'CloudAppSecurity'" `
    -Headers $headers -Method GET

Write-Host "MDA-created network indicators (blocked apps):" -ForegroundColor Cyan
$indicators.value | Select-Object -First 20 | ForEach-Object {
    [PSCustomObject]@{
        URL          = $_.indicatorValue   # The blocked URL or domain
        Action       = $_.action           # Block, Warn, or Allow
        Severity     = $_.severity         # Informational, Low, Medium, High
        Title        = $_.title            # Description of the indicator
        GenerateAlert = $_.generateAlert   # Whether alerts are raised on access
    }
} | Format-Table -AutoSize

Write-Host "`nTotal blocked indicators: $($indicators.value.Count)" -ForegroundColor Yellow

# --- Endpoint Blocking Test ---
# Run this section FROM an MDE-onboarded endpoint (not a server).
# A successful block causes a connection error; HTTP 200 means NOT blocked.
$testUrl = "https://blocked-app-example.com"
try {
    $result = Invoke-WebRequest -Uri $testUrl -TimeoutSec 5 -UseBasicParsing
    Write-Host "WARNING: $testUrl was NOT blocked (Status: $($result.StatusCode))" -ForegroundColor Red
} catch {
    Write-Host "CONFIRMED: $testUrl is blocked by MDE network protection" -ForegroundColor Green
}
Pro Tip: Before enabling broad blocking, start with a small set of clearly risky apps (e.g., known data-leak file-sharing sites). Communicate the blocking policy to employees via email, and provide a process for requesting exceptions to avoid shadow IT workarounds.

Step 10 · Generate Executive Reports and Establish Ongoing Governance

Cloud Discovery is not a one-time exercise. Establish a recurring governance process that includes executive reporting, periodic app reviews, and continuous policy refinement.

Generate the Executive Report

  1. Navigate to Cloud Apps > Cloud Discovery > Dashboard
  2. Click Actions > Generate Cloud Discovery executive report
  3. The report is generated as a PDF containing: top discovered apps, risk distribution, category breakdown, and usage trends
  4. Download and share with stakeholders: CISO, compliance team, and department heads

Build a Custom Shadow IT Report with PowerShell

# ---------------------------------------------------------------
# PURPOSE: Generate a comprehensive Shadow IT executive report as
#          both a CSV file and a console summary dashboard.
# WHY: Leadership needs clear metrics to understand the organisation's
#      shadow IT posture: total apps, governance status, risk
#      distribution, and data volumes. This report serves as the
#      monthly deliverable for the Cloud App Governance Owner.
# OUTPUT:
#   1. CSV file with per-app details (name, risk, tag, users, traffic,
#      compliance certs) - suitable for Excel pivot tables.
#   2. Console summary showing total/sanctioned/unsanctioned/high-risk
#      counts - useful for quick status checks.
# ---------------------------------------------------------------

$mdaUrl  = "https://contoso.us3.portal.cloudappsecurity.com/api/v1/"
$token   = "YOUR_API_TOKEN_HERE"
$headers = @{
    "Authorization" = "Token $token"
    "Content-Type"  = "application/json"
}

# Fetch up to 1000 apps sorted by traffic (highest volume first).
# Sorting by traffic highlights apps consuming the most bandwidth.
$body = @{
    filters = @{}
    sortField = "traffic"
    sortDirection = "desc"
    limit = 1000
} | ConvertTo-Json -Depth 5

$allApps = Invoke-RestMethod -Uri "$($mdaUrl)discovered_apps/" `
    -Headers $headers -Method POST -Body $body

# Build a date-stamped report path.
$reportDate = Get-Date -Format "yyyy-MM-dd"
$reportPath = "C:\Reports\ShadowIT-Executive-Report-$reportDate.csv"

# Transform each app into a flat row for the CSV.
$report = $allApps.data | ForEach-Object {
    [PSCustomObject]@{
        "App Name"       = $_.name                  # Cloud app display name
        "Category"       = $_.category               # E.g. Cloud Storage, AI
        "Risk Score"     = $_.risk                    # 1 (worst) to 10 (best)
        "Tag"            = $_.tag                     # Sanctioned / Unsanctioned / Under Review
        "Users"          = $_.users                   # Unique user count
        "Traffic (MB)"   = [math]::Round($_.traffic / 1MB, 2)  # Data volume
        "Transactions"   = $_.transactions            # Total API/web requests
        "Headquarters"   = $_.headquarters            # App vendor's HQ country
        "Compliance"     = ($_.complianceCertifications -join "; ")  # SOC 2, ISO, etc.
    }
}

$report | Export-Csv -Path $reportPath -NoTypeInformation -Encoding UTF8

# Calculate governance summary statistics for the console dashboard.
$totalApps    = $allApps.total
$sanctioned   = ($allApps.data | Where-Object { $_.tag -eq "sanctioned" }).Count
$unsanctioned = ($allApps.data | Where-Object { $_.tag -eq "unsanctioned" }).Count
$underReview  = ($allApps.data | Where-Object { $_.tag -eq "under review" }).Count
$highRisk     = ($allApps.data | Where-Object { $_.risk -le 4 }).Count

Write-Host "----------------------------------------" -ForegroundColor Cyan
Write-Host "   SHADOW IT EXECUTIVE SUMMARY" -ForegroundColor Cyan
Write-Host "----------------------------------------" -ForegroundColor Cyan
Write-Host "  Report Date:        $reportDate"
Write-Host "  Total Discovered:   $totalApps apps"
Write-Host "  Sanctioned:         $sanctioned apps"
Write-Host "  Unsanctioned:       $unsanctioned apps"
Write-Host "  Under Review:       $underReview apps"
Write-Host "  High Risk (=4):     $highRisk apps"
Write-Host "----------------------------------------" -ForegroundColor Cyan
Write-Host "`nDetailed report saved to: $reportPath" -ForegroundColor Green

Establish Ongoing Governance

  • Weekly: Review Cloud Discovery dashboard for newly discovered apps; triage and tag them
  • Monthly: Review all "Under Review" apps and make sanction/block decisions. Generate the executive report
  • Quarterly: Re-evaluate risk score criteria and policy thresholds. Review sanctioned app list for any that should be revoked
  • On-demand: Investigate anomaly detection alerts. Update app tags when business needs change
  • Assign a Cloud App Governance Owner responsible for maintaining the app catalogue and driving app reviews
  • Create a Shadow IT Request Form where employees can request new cloud apps be evaluated and sanctioned

Enable Data Anonymisation (Privacy Compliance)

  1. If your organisation's privacy policies require it, navigate to Settings > Cloud Apps > Cloud Discovery > Anonymisation
  2. Enable Anonymise private information to hash usernames in Cloud Discovery data
  3. This ensures discovery data can be reviewed for security purposes without revealing individual user identities
Pro Tip: Use the executive report as a recurring agenda item in your monthly security review meetings. Showing leadership the reduction in unsanctioned apps over time demonstrates measurable security improvement and justifies the investment in CASB technology.

Summary

What You Accomplished

  • Navigated the Cloud Discovery portal and understood the architecture for snapshot, continuous, and MDE-based discovery
  • Prepared firewall/proxy logs and created snapshot reports for initial shadow IT visibility
  • Analysed discovered applications using risk scores and detailed app profiles
  • Customised risk score criteria to align with your organisation's compliance and security requirements
  • Deployed Docker-based log collectors for continuous, automated Cloud Discovery
  • Integrated with Microsoft Defender for Endpoint for agent-based discovery across all onboarded endpoints
  • Classified apps with Sanctioned/Unsanctioned/Under Review tags and created custom governance labels
  • Created Cloud Discovery anomaly detection policies to alert on new and risky app usage
  • Enabled endpoint-level blocking of unsanctioned apps via MDE network protection
  • Generated executive reports and established an ongoing governance framework

Next Steps

  • Next Lab: Configure App Connectors & OAuth App Control
  • Enable Conditional Access App Control to apply session-level policies (monitor, block downloads, restrict activities)
  • Set up Information Protection policies in MDA to detect and protect sensitive data in cloud apps
  • Integrate MDA alerts with Microsoft Sentinel for unified SIEM/SOAR workflows and cross-product correlation

📚 Documentation Resources

ResourceDescription
Set up Cloud DiscoveryOfficial guide for configuring Cloud Discovery in Microsoft Defender for Cloud Apps
Create snapshot Cloud Discovery reportsUpload firewall/proxy logs to generate point-in-time shadow IT reports
Working with discovered appsAnalyse, tag, and govern apps identified by Cloud Discovery
Cloud Discovery anonymisationAnonymise user data in Cloud Discovery for privacy compliance
Cloud app risk scoresUnderstand how risk scores are calculated and how to customise scoring criteria
Cloud Discovery policiesCreate app discovery and anomaly detection policies for proactive monitoring
Integrate with Microsoft Defender for EndpointEnable agent-based Cloud Discovery and app blocking via MDE
Configure automatic log upload using Docker on LinuxDeploy Docker-based log collectors for continuous Cloud Discovery
← All Labs Next Lab →