This check flags Azure Storage accounts that allow TLS 1.0 or 1.1 for data-in-transit. Old TLS versions have known cryptographic weaknesses and break most compliance baselines. Fix it by setting the account's minimum TLS version to 1.2.
Every request to Azure Blob, File, Queue, and Table storage rides over TLS. The protocol version that negotiation settles on determines how strong that encryption actually is. By default, older Azure Storage accounts will happily accept connections using TLS 1.0 and 1.1, both of which were deprecated years ago. This check, storage_oldtls, looks at the minimumTlsVersion property on each storage account and fails any account that does not require TLS 1.2 or higher.
If you have never explicitly set this property, there is a good chance some of your accounts are still permitting legacy protocols. Let's walk through what that exposes, how to lock it down, and how to keep it that way.
What this check detects
Azure Storage accounts have a property called minimumTlsVersion. It controls the lowest TLS version a client is allowed to use when talking to the account's data endpoints. The valid values are TLS1_0, TLS1_1, and TLS1_2.
The check fails when an account is set to TLS1_0 or TLS1_1, or when the property is unset on an older account where the platform default still allows downlevel protocols. A passing account explicitly requires TLS1_2.
Note: Storage accounts created through the portal more recently default to TLS 1.2, but accounts provisioned years ago, imported via migration, or created through older API versions may still carry a permissive setting. The setting is also account-wide, so it covers Blob, File, Queue, and Table endpoints in one place.
Why it matters
TLS 1.0 dates to 1999 and TLS 1.1 to 2006. Both have been formally deprecated by the IETF (RFC 8996) and are unsupported by every major browser. The reasons are practical, not academic:
- Weak cipher suites. TLS 1.0 and 1.1 permit ciphers and constructs that are no longer considered safe, including the CBC padding behavior exploited by BEAST and the downgrade weaknesses behind POODLE.
- Downgrade attacks. If an account accepts old versions, an attacker positioned on the network path can force a negotiation down to the weakest mutually supported protocol, then attack that weaker channel.
- Compliance failures. PCI DSS has prohibited TLS 1.0 for cardholder data since 2018. SOC 2, HIPAA, FedRAMP, and most internal security baselines now expect TLS 1.2 as a floor. An account accepting 1.0 is an easy audit finding.
The realistic attack scenario is interception. Suppose an application writes customer records to Blob storage and a legacy SDK on one of your VMs negotiates TLS 1.0 because the account allows it. An attacker who can manipulate traffic between that VM and Azure now has a far easier job decrypting or tampering with that data than they would against TLS 1.2. The blast radius is everything that account stores.
Warning: Enforcing TLS 1.2 can break old clients. Anything still using a deeply outdated SDK, an unpatched .NET Framework runtime, or a hand-rolled HTTP client pinned to TLS 1.0 will start failing connections. Inventory your callers before flipping this on in production.
How to fix it
The fix is a single property change. There is no data migration and no downtime for compliant clients.
Azure CLI
az storage account update \
--name mystorageacct \
--resource-group my-rg \
--min-tls-version TLS1_2
Confirm the change:
az storage account show \
--name mystorageacct \
--resource-group my-rg \
--query minimumTlsVersion \
--output tsv
Azure Portal
- Open your storage account in the Azure Portal.
- Under Settings, select Configuration.
- Find Minimum TLS version and set it to Version 1.2.
- Click Save.
PowerShell
Set-AzStorageAccount `
-ResourceGroupName "my-rg" `
-Name "mystorageacct" `
-MinimumTlsVersion "TLS1_2"
Tip: Before enforcing TLS 1.2, check whether any clients are still using old protocols. Enable storage logging and query the tlsVersion field in the metrics, or run a query in Log Analytics against StorageBlobLogs to see the distribution of TLS versions hitting the account. That tells you what will break before it does.
Fixing it across many accounts
If the check fails on dozens of accounts, loop over them rather than clicking through each one:
az storage account list --query "[?minimumTlsVersion!='TLS1_2'].{name:name, rg:resourceGroup}" -o tsv |
while read name rg; do
echo "Updating $name in $rg"
az storage account update --name "$name" --resource-group "$rg" --min-tls-version TLS1_2
done
Warning: The loop above changes every non-compliant account it finds. Run the list query on its own first and review the output, especially in subscriptions that host legacy integrations.
How to prevent it from happening again
A one-time fix decays. New accounts get created, modules get copied, and someone eventually provisions an account through an old template. Bake the requirement into the layers that create infrastructure.
Set it in your IaC
For Terraform, the azurerm_storage_account resource has a min_tls_version argument:
resource "azurerm_storage_account" "data" {
name = "mystorageacct"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
min_tls_version = "TLS1_2"
}
In a Bicep template:
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'mystorageacct'
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
}
}
Enforce it with Azure Policy
Azure has a built-in policy definition, "Storage accounts should have the specified minimum TLS version". Assign it with a Deny effect so non-compliant accounts cannot be created in the first place, and a remediation task to fix existing ones:
az policy assignment create \
--name "require-storage-tls12" \
--display-name "Require TLS 1.2 on storage accounts" \
--policy "fe83a0eb-a853-422d-aac2-1bffd182c5d0" \
--scope "/subscriptions/<subscription-id>" \
--params '{"minimumTlsVersion": {"value": "TLS1_2"}}'
Tip: Pair a Deny assignment for new resources with a Modify or DeployIfNotExists assignment to auto-remediate accounts that already exist. The first stops the bleeding, the second cleans up the backlog without manual effort.
Gate it in CI/CD
Catch the misconfiguration before it ever reaches Azure. Run a scanner like Checkov or tfsec against your Terraform in the pipeline:
# Checkov flags storage accounts without min_tls_version = TLS1_2
checkov -d ./infra --framework terraform
Fail the build on a finding so the pull request cannot merge until the template is fixed.
Best practices
- Always set
min_tls_versionexplicitly. Relying on platform defaults means your security posture changes silently when Microsoft updates them. Be explicit so your intent is documented and enforced. - Treat TLS 1.2 as a floor, not a ceiling. Azure Storage supports TLS 1.2 today and is adding 1.3 support. When 1.3 becomes available on your accounts, plan to move the floor up.
- Combine TLS enforcement with "secure transfer required." The
supportsHttpsTrafficOnlyproperty rejects plain HTTP entirely. Both settings should be on. TLS version controls how strong the encryption is, and secure transfer ensures encryption is used at all. - Audit before enforcing. Use storage logs to confirm no production clients depend on old TLS, then enforce. Surprises here become outages.
- Apply it at scale with policy, not scripts. Manual fixes do not survive contact with a growing environment. Azure Policy keeps every subscription compliant by default and reports drift continuously.
Setting a minimum TLS version is one of the cheapest security wins in Azure. It costs nothing, requires no architecture change, and closes off a whole class of network-level attacks. The only real work is verifying your clients can handle it, and once you have, there is no good reason to leave the door open to TLS 1.0.

