Securing Azure Storage Accounts With AI Before They Leak
Public blob access, shared keys, and open firewalls are the classic Azure storage leaks. Here's how AI audits storage config, decodes network rules, and drafts the lockdown safely.
- #azure
- #ai
- #storage
- #security
- #blob
Someone created a storage account to “quickly share a build artifact” and set the container to public blob access. The build artifact contained a .env file. That account sat with anonymous read enabled on a container, discoverable by anyone who guessed the predictable name, for the better part of a month before a security scan caught it. Nothing was exfiltrated that we could prove, which is the only good news, and the only reason it existed at all is that public access was the path of least resistance and nobody reviewed the account after it was made.
Azure storage is the most over-exposed resource type in most accounts, for the same reason S3 buckets are on AWS: the secure configuration takes more steps than the insecure one, and nobody audits the result. The good news is the entire security posture of a storage account is queryable — public access flags, key auth, TLS version, network rules, encryption. AI is good at reading that config across every account and flagging the dangerous ones. It does not lock down your storage. You run the audit and apply the fix; it finds the leaks you’d never manually look for.
Audit the account-level switches first
A handful of account-level settings decide most of your exposure. Pull them for one account, or loop over all of them:
# The settings that matter most, in one shot
az storage account show --name "$SA" --resource-group "$RG" \
--query "{publicBlob:allowBlobPublicAccess, sharedKey:allowSharedKeyAccess, tls:minimumTlsVersion, httpsOnly:enableHttpsTrafficOnly, defaultAction:networkRuleSet.defaultAction}" \
-o json
# Across every account in the subscription
az storage account list \
--query "[].{name:name, publicBlob:allowBlobPublicAccess, sharedKey:allowSharedKeyAccess, tls:minimumTlsVersion, netDefault:networkRuleSet.defaultAction}" \
-o table
That table is your leak scanner. Pipe it to AI and let it rank the risk:
Prompt: “Here is
az storage account listsecurity config for every account in a subscription. Rank the accounts from most to least exposed. Flag in order of severity: (1) allowBlobPublicAccess true, (2) networkRuleSet defaultAction Allow (open to all networks), (3) allowSharedKeyAccess true, (4) minimumTlsVersion below TLS1_2. For each flagged account explain the concrete risk in one sentence.”
allowBlobPublicAccess: true plus defaultAction: Allow is the combination that leaks data, and it’s trivial to miss across thirty accounts by eye. AI sorts the whole list by danger so you fix the worst first. You verify each flag against whether that account needs the exposure — a static website account legitimately allows public blob — before changing anything.
Containers can be public even when the account isn’t
Disabling public access at the account level is necessary but the account flag doesn’t retroactively scrub container-level public access that predates it. Check the containers:
az storage container list --account-name "$SA" --auth-mode login \
--query "[].{name:name, publicAccess:properties.publicAccess}" -o table
Any container with publicAccess of blob or container is anonymously readable. Feed the list to AI: “Here are the containers and their publicAccess level. Which are anonymously readable, what’s the difference between blob and container access, and which is worse?” container is worse — it allows listing the container contents anonymously, not just reading a known blob, which turns a guess into a directory walk. AI explains the distinction; you decide which containers can be safely locked to private.
Network rules: decode before you lock
The strongest control is a default-deny network rule with explicit allows, but flipping it carelessly cuts off the apps that legitimately use the account. Read the existing rules first:
az storage account network-rule list --account-name "$SA" --resource-group "$RG" \
--query "{default:defaultAction, vnets:virtualNetworkRules[].virtualNetworkResourceId, ips:ipRules[].ipAddressOrRange, bypass:bypass}" \
-o json
Prompt: “Here are a storage account’s network rules. I want to set defaultAction to Deny. Before I do: which VNets and IP ranges are currently allowed, what does the
bypasssetting (e.g. AzureServices) permit, and what would break if I flip to Deny right now? List the services that might lose access and how to allow each one explicitly.”
The bypass setting is the subtle one — it can silently allow Azure platform services through your deny rule, which may be intended (Azure Monitor needs it) or a gap. AI reads the implications; the real fix is to move to private endpoints so the account isn’t reachable from the public internet at all:
Prompt: “Walk me through replacing public network access on this storage account with a private endpoint for the blob service: the
azcommands to create the endpoint, the Private DNS zone (privatelink.blob.core.windows.net) it needs, and the verification that my app subnet resolves the account to the private IP afterward.”
That private-endpoint pattern, and the Private DNS resolution gotcha that comes with it, is the same one that bites people in VNet and NSG connectivity debugging — worth understanding once and reusing.
Kill shared-key auth and lean on Entra ID
Shared keys are the worst credential in Azure storage: two static, full-power keys per account that grant complete access and are a nightmare to rotate cleanly. The modern posture is allowSharedKeyAccess: false and Entra ID (Azure AD) auth with RBAC data roles. But flip it and any app, SAS token, or tool still using the key breaks instantly:
# Confirm nothing critical still depends on shared key before disabling
az storage account update --name "$SA" --resource-group "$RG" \
--allow-shared-key-access false # do this ONLY after verifying
Prompt: “I want to set allowSharedKeyAccess to false. List everything that breaks: SAS tokens signed with the account key, connection strings using AccountKey, and tools authenticating with the key. For each, give me the Entra ID / RBAC equivalent — e.g. user-delegation SAS,
Storage Blob Data Contributorrole — and the verification step to confirm my apps work on Entra auth BEFORE I disable the key.”
User-delegation SAS (signed with Entra credentials, not the account key) is the replacement most people don’t know exists. AI maps each shared-key usage to its Entra equivalent reliably; you run the verification that proves the apps survive the cutover. Never disable shared key on a live account without that test green.
Stay in control
The pattern is consistent: AI audits the config and decodes the network and auth rules, you verify the exposure is unintended and apply the lockdown. Storage is high-stakes enough that you keep the model on read-and-explain and keep the apply behind a human and a test — a network rule or shared-key change applied blind takes an app offline. The loop: scan account-level switches and rank by exposure, check container-level public access, decode network rules before flipping to Deny, replace shared keys with Entra auth after verifying, and prefer private endpoints over IP allowlists. Run that across every account and the public .env-in-a-blob story never gets written.
My storage-hardening prompts are in the prompts library, and there’s more Azure security material in the Azure category. Every storage leak I’ve seen was queryable config that nobody read. Let the model read it, keep your hands on the lockdown, and the bucket stays closed.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.