Skip to main content
cd ../config-traps
risk/register/container-public-access-outlives-the-account-level-toggle.html
Azure Storage Securityhigh severityAzure Storage

Container Public Access Outlives the Account-Level Toggle

Overview

Flipping AllowBlobPublicAccess to false at the storage account gates new requests but never touches the publicAccess property already set on individual containers. Azure Policy audits report compliant while dormant Container or Blob access settings sit ready to reactivate the moment the account flag is reset by a redeploy.

At a glance

Unsafe setting
Account-level AllowBlobPublicAccess is set false while container-level publicAccess properties still hold Container or Blob from a prior configuration.
Failure trigger
A redeploy, migration, or template missing the explicit false override resets the account flag to its true default, reactivating every container's stale public access setting instantly.
Blast radius
Every container that was ever configured for anonymous access becomes publicly readable again the moment the account-level gate reopens, with no deployment log flagging it.
Recommended control
Explicitly pin allow_nested_items_to_be_public/allowBlobPublicAccess false in every template and separately audit and reset container-level publicAccess to none via CLI or policy.

The Trap

Storage account-level lockdown of blob public access masking container-level publicAccess settings that were never reverted.

The Default State

ARM, Bicep and the Azure Portal all provision new storage accounts with allowBlobPublicAccess set to true unless explicitly overridden. Terraform’s azurerm_storage_account resource carries the same behaviour through allow_nested_items_to_be_public, which defaults to true if the argument is omitted. Engineers who later harden the account by setting the flag to false assume the job is done, because the portal’s container listing stops showing an ‘Anonymous’ badge and Azure Policy’s built-in definition ‘Storage accounts should have public access disabled’ reports compliant. What nobody checks is the publicAccess property stored on each individual container, which remains set to Container or Blob from whenever it was configured, untouched by the account-level change.

The Blast Radius

The account-level flag is a gate, not a wipe. If a subsequent Bicep or Terraform apply redeploys the storage account without explicitly carrying allowBlobPublicAccess: false forward, or if a migration script recreates the resource from a template missing that property, the flag reverts to its true default and every container still holding its old Container or Blob publicAccess setting is instantly and silently exposed to anonymous read again, with no change ticket, no alert, and no deployment log entry mentioning access control. Compliance dashboards built solely on the account-level policy definition never flagged the residual container state, so the exposure looks like a fresh incident when it was actually pre-loaded weeks earlier.

The Lead Mechanic Fix

Treat account-level and container-level access as two separate controls requiring two separate remediations. First, lock the account: az storage account update –name <account> –resource-group <rg> –allow-blob-public-access false, and enforce it fleet-wide with an Azure Policy deny effect on Microsoft.Storage/storageAccounts allowBlobPublicAccess. Second, audit every container regardless of account state: az storage container list –account-name <account> –query “[?properties.publicAccess!=null].{name:name,access:properties.publicAccess}” -o table, then force each result to none with az storage container set-permission –name <container> –account-name <account> –public-access off. Pin allow_nested_items_to_be_public = false explicitly in every Terraform module rather than relying on inherited defaults, and add the policy definition ‘Storage account containers should not allow anonymous access’ to the same initiative so container-level drift is caught independently of the account flag.

Container Public Access Outlives the Account-Level Toggle | Config Traps