How to block public access to Azure storage accounts
This guide shows how to find Azure storage accounts that allow anonymous blob reads or accept traffic from any network, then close both gaps without breaking the apps that use them. It is written for Azure administrators who look after storage accounts across one or more subscriptions.
Before you start
Two separate settings decide whether a storage account is exposed. One controls anonymous reads of blob data. The other controls which networks can reach the public endpoint. This guide covers both.
- Role to look: Reader on the subscriptions you want to review.
- Role to change: a role that includes
Microsoft.Storage/storageAccounts/write, such as Storage Account Contributor, Contributor or Owner, scoped to the storage account or higher. - Licences: none are needed for these settings.
- Tools: the Azure portal and Azure CLI (
az loginfirst). Azure Resource Graph Explorer is handy for reviewing many accounts at once. - Make a list of apps first. Anything that reads blobs without credentials, or connects from an address you have not allowed, will stop working after the change.
Step by step
Steps 1 and 2 only read information. Steps 3 to 5 change the storage account and are marked CHANGE.
- List the accounts and their current setting (read-only). In the Azure portal open Resource Graph Explorer and run:
An empty (null)resources | where type =~ 'Microsoft.Storage/storageAccounts' | extend allowBlobPublicAccess = parse_json(properties).allowBlobPublicAccess | extend publicNetworkAccess = parse_json(properties).publicNetworkAccess | extend defaultAction = parse_json(properties).networkAcls.defaultAction | project subscriptionId, resourceGroup, name, allowBlobPublicAccess, publicNetworkAccess, defaultActionallowBlobPublicAccessmeans the property was never set on that account, so treat it as not yet locked down. - Find out who still reads anonymously (read-only). On each account that allows it, open Monitoring > Metrics, choose the Blob namespace and the Transactions metric, then filter Authentication equal to Anonymous. For detail, send the blob resource logs to a Log Analytics workspace through a diagnostic setting and run:
If a container really must be public, move it to its own storage account and leave that account out of this clean-up.StorageBlobLogs | where TimeGenerated > ago(7d) and AuthenticationType == "Anonymous" | project TimeGenerated, AccountName, AuthenticationType, Uri - CHANGE: disallow anonymous blob access. Portal: open the storage account, go to Settings > Configuration and set Allow Blob anonymous access to Disabled. Or with the CLI:
This overrides every container setting in the account. Allow up to 30 seconds for it to take effect. A static website in theaz storage account update --name <storage-account> --resource-group <resource-group> --allow-blob-public-access false$webcontainer is not affected. - CHANGE: add your allowed sources, then deny everything else. Add the networks that must keep access before you flip the default, so you do not lock yourself out:
For a subnet rule to work, the subnet also needs the Azure Storage service endpoint. The portal creates it for you; the CLI does not.az storage account network-rule add --resource-group <resource-group> --account-name <storage-account> --ip-address <office-public-ip-or-cidr> az storage account network-rule add --resource-group <resource-group> --account-name <storage-account> --vnet-name <vnet> --subnet <subnet> az storage account update --name <storage-account> --resource-group <resource-group> --default-action Deny - CHANGE (optional, strictest): turn off the public endpoint. If every client reaches the account through a private endpoint, set public network access to disabled:
Do this only after the private endpoint and its DNS are working.az storage account update --name <storage-account> --resource-group <resource-group> --public-network-access Disabled - Keep it that way. Assign an Azure Policy definition with the audit effect on
Microsoft.Storage/storageAccounts/allowBlobPublicAccessfirst. Once the estate is clean, switch the effect to deny so new accounts cannot be created with public access.
Check it worked
These commands only read. Run them after the changes and expect the values shown.
az storage account show --name <storage-account> --resource-group <resource-group> --query "{anonymous:allowBlobPublicAccess, publicNetwork:publicNetworkAccess, defaultAction:networkRuleSet.defaultAction}" --output json
az storage account network-rule list --account-name <storage-account> --resource-group <resource-group>
You want anonymous to be false and defaultAction to be Deny (or publicNetwork to be Disabled). Then repeat the Resource Graph query from step 1 and confirm no account still shows true or an empty value. An anonymous request to a blob URL from a machine outside your allowed networks should now be refused.
Common mistakes
- Setting Deny before adding your own IP or subnet, which cuts off your own admin access to the data plane.
- Disabling anonymous access without checking the anonymous request metric, so a public download link or website asset quietly stops working.
- Assuming an empty
allowBlobPublicAccessmeans safe. It means the property was never set. - Adding an IP rule for a subnet that already uses a service endpoint. Traffic from that subnet uses a private source address, so the IP rule has no effect.
- Turning off the public endpoint before private endpoint DNS resolves correctly.
- Treating one account as the whole job. Repeat the review for every subscription.
How ConfigCobra helps
ConfigCobra is read-only: it never changes your Azure. Its Azure scan covers the CIS Microsoft Azure Foundations Benchmark v6.0.0 (127 controls) and three further Azure benchmarks, 339 controls in total, using Reader on your subscriptions and a certificate-based app registration. Each finding points at the exact Azure resource, and the Azure inventory has a built-in view of resources with public network access.
For a failing control, remediation impact analysis shows what would be affected before you apply the fix, with an impact level of High, Medium, Low or None. Scheduled scans with drift detection alert you by email when a control regresses, for example when someone re-enables anonymous access.
Related checks
Go further
- All guidesAdmin how-tos for Microsoft 365, Azure, Google Cloud and AWS
- Check libraryEvery CIS check, explained
- RemediationSee what a fix changes in your tenant before you apply it, then use the generated script
- Inventory & InsightsEvery user, app, device and resource in one inventory, with built-in risk views
See where your Microsoft Azure stands
A read-only assessment against the CIS benchmarks, prioritised so you know what to fix first.