Azure Public Exposure Scanner
On a schedule, the flow uses Azure Resource Manager to find resources exposed to the internet — public IPs, NSG rules allowing 0.0.0.0/0 on sensitive ports, storage accounts with public access, public-endpoint databases — scores the risk, logs findings to Dataverse, and posts high-risk exposures to a security Teams channel. Catches misconfigurations before attackers do.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
FlowLibs - Azure Public Exposure Scanner (CF-522) is a scheduled cloud flow that uses Azure Resource Manager (ARM) to continuously detect internet-facing misconfigurations across an Azure subscription. Each morning it enumerates Network Security Groups, Public IP Addresses, and Storage Accounts, flags resources exposed to the public internet (NSG rules open to 0.0.0.0/0 on sensitive ports, public IPs, storage accounts allowing public blob access), scores the risk, logs every finding to a Dataverse table, and posts the high-risk exposures to a security Microsoft Teams channel.
Why it matters: Accidental public exposure is a top breach cause. A scheduled owned scan surfaces risky configurations quickly — before an attacker finds them — without waiting for an audit cycle.
> Built API-first as a Dataverse workflow record. Ships Off. Going live requires only: authorize the Dataverse + Teams connections, set the five new environment variable values (and confirm the reused ARM service-principal env vars), and turn the flow on.
Use Case
A security / platform team wants continuous detection of internet-facing misconfigurations across the Azure estate, prioritized by a risk score, surfaced into the channels they already watch (a Dataverse findings log for trend analysis and a Teams channel for immediate triage).
Flow Architecture
Daily Exposure Sweep 0500
Recurrence (daily, 05:00 UTC)Built-in trigger, no connection
Initialize Correlation Id
Initialize Variableguid(), stamped on every finding row and alert for cross-run tracing
Initialize Scan Date
Initialize VariableutcNow(), recorded on every finding
Initialize Sensitive Ports
Initialize VariableFrom flowlibs_SensitivePorts
Initialize Risk Threshold
Initialize Variableint(flowlibs_RiskAlertThreshold); minimum score that triggers a Teams alert
Initialize Arm Scope
Initialize Variableconcat(ArmBaseUrl, '/subscriptions/', AzureSubscriptionId); base of every ARM query
Initialize Sec Group Id / Initialize Sec Channel Id
Initialize VariableThe security Teams target
List Network Security Groups
HTTP GET (ARM + ActiveDirectoryOAuth)GET {scope}/providers/Microsoft.Network/networkSecurityGroups (api 2023-09-01)
List Public IP Addresses
HTTP GET (ARM + ActiveDirectoryOAuth)GET {scope}/providers/Microsoft.Network/publicIPAddresses (api 2023-09-01)
List Storage Accounts
HTTP GET (ARM + ActiveDirectoryOAuth)GET {scope}/providers/Microsoft.Storage/storageAccounts (api 2023-01-01)
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_ArmBaseUrl | String | https://management.azure.com | ARM base URL (sovereign-cloud portable) (reused) |
| flowlibs_AzureSubscriptionId | String | <configure> | Target subscription to scan (reused) |
| flowlibs_AzureTenantId | String | <your-tenant-id> | AAD tenant for the service principal (reused) |
| flowlibs_AzureClientId | String | <configure> | Service-principal client id (reused) |
| flowlibs_AzureClientSecret | String | <configure> | Service-principal secret (reused) |
| flowlibs_SensitivePorts | String | 22,3389,1433,3306,5432 | Ports that escalate NSG risk (new) |
| flowlibs_ExposureTable | String | flowlibs_exposurefindings | Findings table (entity set name) (new) |
| flowlibs_SecTeamsGroupId | String | <your-team-id> | Security Teams group/team id (new) |
| flowlibs_SecTeamsChannelId | String | <your-channel-id> | Security Teams channel id (new) |
| flowlibs_RiskAlertThreshold |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Microsoft Dataverse | shared_commondataserviceforapps | CreateRecord |
| Microsoft Teams | shared_teams | PostMessageToConversation |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Auto-remediate
- Add an Approval before tightening an offending NSG rule to a known CIDR via an ARM PUT.
- Allowlist
- Exclude intentionally-public resources by checking a resource tag in each loop.
- Defender for Cloud
- Cross-reference secure-score recommendations via ARM for richer findings.
- Cadence / threshold
- Change the Recurrence schedule and flowlibs_RiskAlertThreshold without touching flow logic.
- More resource types
- Add ARM list calls (App Services, SQL servers, Cosmos public endpoints) following the same loop pattern.
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Open-to-world rule
Matches inbound Allow rules whose source is the public internet.
EXPR.02Sensitive-port hit / score
Scores 90 for a sensitive open port, 55 otherwise.
EXPR.03Risk level
Maps a numeric score to a High/Medium/Low band.
EXPR.04ARM scope
Base scope reused by every ARM query.
EXPR.05ARM OAuth audience
Derives the OAuth audience from the ARM base URL.
EXPR.06Alert gate
True when a finding's score meets the alert threshold.
Customize & download
Generate a ready-to-import copy of this solution with your environment-variable values baked in — available on Base, Pro, or Team.
Upgrade to customize
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.