To find SQL servers open to the internet in Azure, check 2 things on each server: whether public network access is on, and whether a firewall rule lets in every address. A rule from 0.0.0.0 to 255.255.255.255 lets in every address, while a rule from 0.0.0.0 to 0.0.0.0 lets in only Azure's own services. Resource Graph can lag a recent change, so confirm a server in the Azure portal before you act.
| Method | Best fit |
|---|---|
| PowerShell | A repeatable CSV of every SQL server across selected subscriptions, with the firewall rules read for you. |
| Admin centre | A quick look at server settings in Resource Graph Explorer, then the firewall rules of chosen servers. |
| Altivane | Recommended for recurring reviews across readable subscriptions, with firewall rules and weak points in a workbook. |
Before you start
Use an Azure account with read access to the subscriptions you want to check. The built-in Reader role is enough, assigned at the subscription or a parent scope, and no separate licence is needed. The script uses the Az.Accounts, Az.ResourceGraph and Az.Sql modules.
The check covers Azure SQL Database servers, not SQL Managed Instance or SQL Server on a virtual machine. A server is reachable from the internet only when public network access is on and a firewall rule admits the caller's address. Rules set on a single database, which only T-SQL can create, are not read. The examples read and export and change nothing.
Option 1
Find SQL servers open to the internet with PowerShell
Install the modules once and connect to Azure. The script reads each server's settings from Resource Graph, then its firewall rules, and writes 1 row per server with the servers open to every address first.
Install-Module -Name Az.Accounts, Az.ResourceGraph, Az.Sql -Repository PSGallery -Scope CurrentUser$ErrorActionPreference = 'Stop'
Connect-AzAccount -ErrorAction Stop
try {
$subscriptionIds = @(
Get-AzSubscription |
Where-Object State -eq 'Enabled' |
Select-Object -ExpandProperty Id
)
if ($subscriptionIds.Count -eq 0) {
Write-Output 'No enabled subscriptions are available to this account.'
return
}
$query = @(
'Resources'
'| where type =~ "microsoft.sql/servers"'
'| project id, name, subscriptionId, resourceGroup,'
' serverName = tostring(properties.fullyQualifiedDomainName),'
' publicAccess = tostring(properties.publicNetworkAccess),'
' minimumTls = tostring(properties.minimalTlsVersion),'
' entraOnly = tobool(properties.administrators.azureADOnlyAuthentication),'
' entraAdmin = tostring(properties.administrators.login)'
'| order by publicAccess desc, id asc'
) -join ' '
function Get-AzGraphRows {
param([string]$Query, [string[]]$SubscriptionIds)
$results = [System.Collections.Generic.List[object]]::new()
$skipToken = $null
do {
$arguments = @{
Query = $Query
Subscription = $SubscriptionIds
First = 1000
}
if ($skipToken) { $arguments.SkipToken = $skipToken }
$page = Search-AzGraph @arguments
foreach ($record in $page.Data) { $results.Add($record) }
$skipToken = $page.SkipToken
} while ($skipToken)
return $results.ToArray()
}
$servers = @(Get-AzGraphRows -Query $query -SubscriptionIds $subscriptionIds)
if ($servers.Count -eq 0) {
Write-Output 'No SQL servers were found in the readable subscriptions.'
return
}
$rows = @(foreach ($server in $servers) {
$rules = $null
try {
Set-AzContext -SubscriptionId $server.subscriptionId | Out-Null
$ruleArguments = @{
ResourceGroupName = $server.resourceGroup
ServerName = $server.name
}
$rules = @(Get-AzSqlServerFirewallRule @ruleArguments)
} catch {
Write-Warning "Rules not read for $($server.name): $($_.Exception.Message)"
}
$ruleCount = 'Not read'
$everyAddress = 'Not read'
$azureServices = 'Not read'
if ($null -ne $rules) {
$wide = @($rules | Where-Object {
$_.StartIpAddress -eq '0.0.0.0' -and $_.EndIpAddress -eq '255.255.255.255'
})
$azureOnly = @($rules | Where-Object {
$_.StartIpAddress -eq '0.0.0.0' -and $_.EndIpAddress -eq '0.0.0.0'
})
$ruleCount = $rules.Count
$everyAddress = if ($wide.Count -gt 0) { 'Yes' } else { 'No' }
$azureServices = if ($azureOnly.Count -gt 0) { 'Yes' } else { 'No' }
}
$entraOnly = if ($server.entraOnly) { 'Yes' } else { 'No' }
$entraAdmin = if ($server.entraAdmin) { $server.entraAdmin } else { 'None' }
[pscustomobject]@{
ServerName = $server.serverName
SubscriptionId = $server.subscriptionId
ResourceGroup = $server.resourceGroup
PublicNetworkAccess = $server.publicAccess
MinimumTls = $server.minimumTls
EntraOnlySignIn = $entraOnly
EntraAdministrator = $entraAdmin
FirewallRules = $ruleCount
OpenToEveryAddress = $everyAddress
AzureServicesAllowed = $azureServices
}
})
$rows | Sort-Object OpenToEveryAddress -Descending |
Export-Csv -Path './sql-servers.csv' -NoTypeInformation -Encoding utf8
$open = @($rows | Where-Object OpenToEveryAddress -eq 'Yes')
$public = @($rows | Where-Object PublicNetworkAccess -eq 'Enabled')
Write-Output "$($rows.Count) SQL servers saved to sql-servers.csv."
Write-Output "$($open.Count) have a firewall rule open to every address."
Write-Output "$($public.Count) have public network access enabled."
} finally {
Disconnect-AzAccount | Out-Null
}The export has each server's address, subscription, resource group, public network access, minimum TLS version, Entra settings, firewall rule count, and whether a rule lets in every address or Azure's services. A server whose rules cannot be read shows Not read and a warning.
To narrow the search, keep only the subscription IDs you want in $subscriptionIds. To keep only servers that can be reached, filter $rows on PublicNetworkAccess before the export. Keep id in the query, because Azure's continuation token needs it.
Option 2
Find open SQL servers in the Azure portal
- Sign in to the Azure portal with read access to the subscriptions.
- Search for resource graph > choose Resource Graph Explorer.
- Paste this query in Query 1 and choose Run query:
Resources | where type =~ 'microsoft.sql/servers' | project id, name, subscriptionId, resourceGroup, serverName = tostring(properties.fullyQualifiedDomainName), publicAccess = tostring(properties.publicNetworkAccess), minimumTls = tostring(properties.minimalTlsVersion), entraOnly = tobool(properties.administrators.azureADOnlyAuthentication), entraAdmin = tostring(properties.administrators.login) | order by publicAccess desc, id asc - Review the servers in Results. Servers with public network access
Enabledcome first; only those can be reached from the internet. - To read a server's firewall rules, search for SQL servers, choose the server, then under Security choose Networking and open the Public access tab.
- Look for a rule running from 0.0.0.0 to 255.255.255.255, which lets in every address. The Allow Azure services and resources to access this server setting is the rule from 0.0.0.0 to 0.0.0.0.
This suits a one-off check or a few servers, since the rules are opened server by server. For more, use PowerShell.
Option 3 ยท Recommended for recurring reviews
Find open SQL servers with Altivane
- Open Altivane and choose the tenant.
- Run the Azure SQL Servers report.
- Open the Excel workbook and read the Azure SQL servers table, with each server's public network access, TLS version, Entra settings, database count and firewall rules.
- Read the Open to every address column and the findings column to see which servers are open and why.
Altivane reads every subscription the connected account can read in 1 run, reads each server's firewall rules for you, and writes an Excel workbook to keep. The findings name what is weak: open to every address, public network access on, TLS below 1.2, SQL sign-in allowed, or no Entra administrator. A server whose rules could not be read shows Not read, and the report says it is partial.
Contact usCommon questions
Does Allow Azure services mean a server is open to the internet?
No, but it is wider than most people want. The setting adds a rule from 0.0.0.0 to 0.0.0.0, which lets in every resource inside Azure, whether or not it is in your subscription. The script and report show it separately from a rule open to every address.
Are database-level firewall rules included?
No. Server-level rules, which apply to every database on the server, are the ones read. Database-level rules apply to 1 database and can only be created with T-SQL, so a server with no wide server rule can still admit addresses through them.
What else should I review for internet exposure?
For network security group rules open to the internet, see how to find NSG rules open to the internet in Azure.
Microsoft references
Technical references checked on 8 October 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.