Theoretical Foundation: Configure Storage Account Encryption
1. Initial Intuitionβ
Imagine you store confidential documents in a safe. Anyone who has the safe's key can open and read the documents. Now imagine that before putting the documents in the safe, you scramble the text of each page with a secret code. Even if someone steals the entire safe and forces it open, the documents are unreadable without the code to decode them.
Encryption at rest is exactly that: data is transformed into an unreadable format before being written to disk. When an authorized system needs to read the data, it is automatically decoded in memory.
In Azure Storage, all encryption at rest is automatic, mandatory, and transparent. It cannot be disabled. What you control is who owns and manages the keys used in this process.
2. Contextβ
2.1 Why encryption at rest existsβ
Without encryption at rest, an attacker who gained physical access to datacenter disks could read the data directly. With encryption, physical disks contain only scrambled data, worthless without the corresponding keys.
In Azure, Storage Account encryption protects against:
- Unauthorized physical access to datacenter hardware
- Physical disk exfiltration
- Regulatory requirements (LGPD, GDPR, HIPAA, PCI-DSS) that require encrypted data at rest
2.2 What Storage Account encryption coversβ
| Service | Encrypted? |
|---|---|
| Blob Storage | Yes |
| Azure Files | Yes |
| Queue Storage | Yes |
| Table Storage | Yes |
| Object metadata | Yes |
| Data in transit (HTTPS) | Yes (separate configuration) |
Important point: Azure Storage encryption at rest is different from VM disk encryption (Azure Disk Encryption / Server-Side Encryption of managed disks). They are independent mechanisms.
3. Concept Buildingβ
3.1 How encryption works internallyβ
Azure uses AES-256, one of the most robust cryptographic standards available. The process involves two levels of keys:
Data Encryption Key (DEK): Key that encrypts the data itself. Each object (blob, file, etc.) can have its own DEK.
Key Encryption Key (KEK): Key that encrypts the DEK. This is the level where customer control is applied.
This two-layer model means that:
- If you rotate or revoke the KEK, all data becomes inaccessible without needing to re-encrypt every byte.
- The data itself (protected by the DEK) doesn't need to be moved or re-encrypted when the KEK changes.
3.2 The two key management modelsβ
Microsoft Managed Keys (MMK)β
What it is: Microsoft automatically generates, stores, and rotates the KEKs. You have no access to them.
Analogy: It's like using the hotel safe. The hotel manages the combination, you use the service without worrying about the master key.
Characteristics:
- Enabled by default on all Storage Accounts
- No additional management cost
- Automatic rotation managed by Microsoft
- No visibility or control over keys
Customer Managed Keys (CMK)β
What it is: You create and manage the KEK in an Azure Key Vault or Azure Managed HSM. Azure Storage uses this external key to encrypt the DEKs, but never stores your key permanently.
Analogy: It's like having a bank safe where only you have the master key. The bank can access the contents when you allow it, but if you revoke access, the bank can no longer open the safe.
Characteristics:
- Full control over key lifecycle
- Ability to revoke access immediately
- Complete audit of key usage (who used it, when)
- Customer-controlled key rotation
- Additional cost: Key Vault and key operations are charged
Customer Provided Keys (CPK)β
What it is: The customer provides the key directly in each HTTP request to Storage. The key is never stored in Azure.
Analogy: It's like you personally bringing the key every time you want to open the safe. The bank never keeps your key, but you need to bring it every time.
Characteristics:
- Key exists only in memory during the request
- Not stored in Azure
- Management completely outside Azure
- Applicable only to Blob Storage operations
- Requires implementation in application code (not configurable in portal)
3.3 Encryption scope: account vs infrastructureβ
Azure offers two scopes where CMK can be applied:
Account scope (CMK default): One Key Vault key encrypts the entire Storage Account data.
Encryption scope (Encryption Scope): Allows using different keys per container or per individual blob, within the same Storage Account.
Encryption Scopes allow cryptographic isolation within the same Storage Account. HR department and finance data can coexist in the same Storage Account with completely different keys.
3.4 Infrastructure Encryption (Double Encryption)β
For compliance scenarios that require multiple encryption layers, Azure offers Infrastructure Encryption, which adds a second encryption layer at the infrastructure level (hardware/hypervisor), independent of service encryption.
Result: data is encrypted twice with completely independent algorithms and keys.
| Layer | Algorithm | Keys |
|---|---|---|
| Layer 1 (Storage Service) | AES-256 | MMK or CMK |
| Layer 2 (Infrastructure) | AES-256 | Always Microsoft-managed |
Important: Infrastructure Encryption must be enabled at Storage Account creation time. It cannot be enabled later.
4. Structural View: Write and Read Flowβ
5. Practical Operationβ
5.1 Prerequisites for CMKβ
To use Customer Managed Keys, you need:
-
Azure Key Vault with the following configurations:
- Soft Delete enabled (mandatory)
- Purge Protection enabled (mandatory for CMK in Storage)
- Configured in the same region as the Storage Account (recommended)
-
Managed Identity assigned to the Storage Account so it can access the Key Vault without explicit credentials.
-
Key Vault permissions for the Storage Account identity:
Get(get key)Wrap Key(encrypt DEK with KEK)Unwrap Key(decrypt DEK with KEK)
5.2 Types of managed identity for CMKβ
| Type | Description | When to use |
|---|---|---|
| System-assigned Managed Identity | Created and linked to the Storage Account. Deleted along with the account. | Simple scenarios, one account per Key Vault |
| User-assigned Managed Identity | Created independently, can be shared between resources. | Multiple Storage Accounts using the same Key Vault |
5.3 Key rotationβ
When you rotate the KEK in Key Vault (create a new key version), Azure Storage needs to be notified to start using the new version.
Automatic rotation: Configure the Storage Account to use the latest version of the key. Azure automatically detects new versions and adopts them.
Manual rotation: Configure the Storage Account to use a specific version of the key. You control exactly when the new version is adopted.
Critical behavior on revocation: If you revoke the Storage Account identity's access to Key Vault (or delete the key), the Storage Account becomes inaccessible immediately. Reads and writes fail with authorization error. This is intentional and is the "kill switch" mechanism that CMK provides.
5.4 Encryption Scopes: lifecycleβ
Critical point: Encryption Scopes cannot be deleted, only disabled. A disabled scope makes blobs that use it inaccessible, but doesn't delete them. Re-enabling the scope restores access.
6. Implementation Methodsβ
6.1 Enabling CMK via Azure Portalβ
Steps:
- Create Key Vault with Soft Delete and Purge Protection
- Create a key in Key Vault (RSA 2048, 3072, or 4096)
- In Storage Account:
Security + networking > Encryption - Select "Customer-managed keys"
- Select Key Vault and key
- Assign or create Managed Identity for the account
- Save
The portal automatically configures Key Vault permissions for the Storage Account identity when you select "Grant access" during the process.
6.2 Azure CLIβ
Creating Key Vault with mandatory protections:
az keyvault create \
--name myKeyVault \
--resource-group myRG \
--location eastus \
--enable-soft-delete true \
--enable-purge-protection true
Creating RSA key:
az keyvault key create \
--vault-name myKeyVault \
--name myStorageKey \
--kty RSA \
--size 2048
Assigning System-assigned Identity to Storage Account:
az storage account update \
--name mystorageaccount \
--resource-group myRG \
--assign-identity
Getting the Principal ID of the identity:
PRINCIPAL_ID=$(az storage account show \
--name mystorageaccount \
--resource-group myRG \
--query identity.principalId \
--output tsv)
Granting Key Vault permissions:
az keyvault set-policy \
--name myKeyVault \
--object-id $PRINCIPAL_ID \
--key-permissions get wrapKey unwrapKey
Configuring CMK on Storage Account:
KEY_URI=$(az keyvault key show \
--vault-name myKeyVault \
--name myStorageKey \
--query key.kid \
--output tsv)
az storage account update \
--name mystorageaccount \
--resource-group myRG \
--encryption-key-source Microsoft.Keyvault \
--encryption-key-uri $KEY_URI
6.3 Azure PowerShellβ
# Create identity and get principal
$storageAccount = Set-AzStorageAccount `
-ResourceGroupName "myRG" `
-Name "mystorageaccount" `
-AssignIdentity
# Configure Key Vault permissions
Set-AzKeyVaultAccessPolicy `
-VaultName "myKeyVault" `
-ObjectId $storageAccount.Identity.PrincipalId `
-PermissionsToKeys get, wrapKey, unwrapKey
# Get key
$key = Get-AzKeyVaultKey `
-VaultName "myKeyVault" `
-Name "myStorageKey"
# Configure CMK
Set-AzStorageAccount `
-ResourceGroupName "myRG" `
-Name "mystorageaccount" `
-KeyvaultEncryption `
-KeyName $key.Name `
-KeyVersion $key.Version `
-KeyVaultUri "https://myKeyVault.vault.azure.net"
6.4 Bicepβ
// Key Vault with mandatory protections
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: 'myKeyVault'
location: location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableSoftDelete: true
enablePurgeProtection: true
accessPolicies: []
}
}
// Storage Account with system-assigned identity and CMK
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'mystorageaccount'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
identity: {
type: 'SystemAssigned'
}
properties: {
encryption: {
keySource: 'Microsoft.Keyvault'
keyvaultproperties: {
keyname: 'myStorageKey'
keyvaulturi: keyVault.properties.vaultUri
}
}
}
}
Circular dependency in Bicep/ARM: The Storage Account needs the Key Vault to exist, but the access policy in Key Vault needs the Principal ID from the Storage Account. This requires two deployments or using RBAC on Key Vault (recommended) instead of Access Policies.
6.5 Creating Encryption Scopesβ
Via CLI:
# Scope with MMK
az storage account encryption-scope create \
--account-name mystorageaccount \
--resource-group myRG \
--name FinanceScope \
--key-source Microsoft.Storage
# Scope with CMK
az storage account encryption-scope create \
--account-name mystorageaccount \
--resource-group myRG \
--name HRScope \
--key-source Microsoft.KeyVault \
--key-uri $KEY_URI
Applying scope to a container:
az storage container create \
--account-name mystorageaccount \
--name employee-data \
--default-encryption-scope HRScope \
--prevent-encryption-scope-override true
The --prevent-encryption-scope-override true parameter prevents individual blobs within the container from using a different scope than the one defined in the container.
7. Control and Securityβ
7.1 RBAC vs Access Policies in Key Vaultβ
Azure Key Vault supports two access control models:
| Model | Recommended? | How it works |
|---|---|---|
| Access Policies | Legacy | Policies directly on Key Vault per principal |
| Azure RBAC | Yes (preferred) | Azure Resource Manager roles applied to Key Vault |
With RBAC, assign the Key Vault Crypto Service Encryption User role to the Storage Account identity. This role grants exactly the necessary permissions (get, wrapKey, unwrapKey) without excess.
az role assignment create \
--role "Key Vault Crypto Service Encryption User" \
--assignee $PRINCIPAL_ID \
--scope $(az keyvault show --name myKeyVault --query id --output tsv)
7.2 The CMK "kill switch"β
One of the main motivators for using CMK is the ability to immediately revoke access to all data:
- Delete the key in Key Vault
- Revoke the Storage Account identity permissions
- Disable the entire Key Vault
Any of these actions makes the Storage Account immediately inaccessible. This is useful in scenarios of:
- Contract termination with customers who have data in the account
- Security incident response
- Regulatory compliance requiring "right to be forgotten"
Attention: Azure doesn't immediately warn about the impact. Test the revocation and restoration procedure in non-production environment before depending on it.
7.3 Key usage auditingβ
With CMK, each operation that uses the key (read, write, rotation) generates a log in Azure Key Vault diagnostic logs. These logs show:
- Which identity used the key
- When it was used
- From which IP
- Whether it was successful or denied
Enable diagnostics on Key Vault:
az monitor diagnostic-settings create \
--name "kv-audit-logs" \
--resource $(az keyvault show --name myKeyVault --query id --output tsv) \
--logs '[{"category":"AuditEvent","enabled":true}]' \
--workspace <log-analytics-workspace-id>
8. Decision Makingβ
8.1 MMK vs CMK vs CPKβ
| Situation | Best choice | Reason |
|---|---|---|
| Internal data without specific regulatory requirement | MMK | Simple, no additional cost, guaranteed durability |
| HIPAA, PCI-DSS, LGPD compliance with key auditing | CMK | Complete auditing and lifecycle control |
| Need for immediate "kill switch" | CMK | Instant revocation via Key Vault |
| Different clients with cryptographic isolation in the same account | CMK + Encryption Scopes | Keys per department/client |
| Key must never leave the client environment | CPK | Key provided by request, never stored in Azure |
| Regulatory requirement for double encryption | Infrastructure Encryption + CMK | Two independent layers |
| Dedicated HSM required by regulation | Azure Managed HSM + CMK | Hardware dedicated to the client |
8.2 When to use Encryption Scopesβ
| Scenario | Use Encryption Scope? | Reason |
|---|---|---|
| Multiple departments with sensitive data in the same account | Yes | Cryptographic isolation per department |
| Account with data from multiple clients | Yes | Key per client, individual revocation |
| Homogeneous account with a single sensitivity level | No | Unnecessary complexity |
| Account with few non-sensitive containers | No | Default MMK is sufficient |
8.3 System-assigned vs User-assigned Identityβ
| Situation | Recommended identity | Reason |
|---|---|---|
| One Storage Account, one Key Vault | System-assigned | Simplicity, tied to the account lifecycle |
| Multiple Storage Accounts in the same Key Vault | User-assigned | One identity, one access policy |
| Automation with Terraform/Bicep | User-assigned | Avoids circular dependency in deployment |
| Portability between accounts | User-assigned | Reusable across multiple resources |
9. Best Practicesβ
- Enable Purge Protection on Key Vault before linking to Storage. Without this, accidental deletion of the Key Vault can make data permanently inaccessible.
- Use User-assigned Managed Identity in automation (IaC) to avoid circular dependencies between Storage Account and Key Vault.
- Prefer Azure RBAC over Access Policies in Key Vault for more granular and auditable management.
- Configure automatic key rotation and set the Storage Account to use the latest version of the key.
- Enable diagnostics on Key Vault and send to Log Analytics for complete key usage auditing.
- Test the revocation procedure in non-production environments before implementing the "kill switch" in production.
- Use Encryption Scopes when multiple data contexts with different regulatory requirements coexist in the same Storage Account.
- Document the dependency between Storage Account and Key Vault. If the Key Vault is moved, deleted, or has permissions changed, the Storage Account stops working.
- Enable Infrastructure Encryption at creation, never after.
10. Common Errorsβ
| Error | Why it happens | How to avoid |
|---|---|---|
| Storage Account inaccessible after Key Vault access change | Identity permissions inadvertently revoked | Test permission changes in non-production environment |
| Cannot enable Purge Protection on Key Vault | Key Vault already exists without Purge Protection and cannot be enabled retroactively in some cases | Create Key Vaults with Purge Protection from the beginning |
| CMK linking failure: "key not found" | Key deleted or specific version expired | Use automatic rotation or validate key URI |
| Infrastructure Encryption not enabled | Forgot to check the option at creation | Use Bicep/ARM templates with the configuration defined |
| Encryption Scope accidentally disabled | Blobs become immediately inaccessible | Control RBAC for Encryption Scope operations |
| Circular dependency in Bicep/ARM | Storage needs Key Vault; Key Vault needs Storage Principal ID | Use User-assigned Identity created before both |
| Using CPK without client implementation | CPK requires application code, not automatic | Only choose CPK if there's implementation capacity in SDK |
| Key Vault and Storage in different regions | Additional latency and risk of failure in case of regional failure | Place Key Vault and Storage in the same region |
11. Operation and Maintenanceβ
11.1 Checking encryption configurationβ
Via CLI:
az storage account show \
--name mystorageaccount \
--resource-group myRG \
--query encryption
Returns the current state: keySource, keyVaultProperties, and if requireInfrastructureEncryption is active.
11.2 Manually rotating the keyβ
# Create new key version in Key Vault
az keyvault key create \
--vault-name myKeyVault \
--name myStorageKey \
--kty RSA \
--size 2048
# Get new version
NEW_KEY_VERSION=$(az keyvault key list-versions \
--vault-name myKeyVault \
--name myStorageKey \
--query "[-1].kid" \
--output tsv)
# Update Storage Account to use new version
az storage account update \
--name mystorageaccount \
--resource-group myRG \
--encryption-key-uri $NEW_KEY_VERSION
11.3 Monitoring Key Vault access issuesβ
Configure alerts in Azure Monitor for:
- Authentication failures in Key Vault: Indicates that the Storage Account identity lost permission.
- Key disabled or expired: Causes immediate Storage Account inaccessibility.
- Key purge: Irreversible event.
az monitor activity-log alert create \
--name "keyvault-key-disabled" \
--resource-group myRG \
--condition category=Administrative and operationName=Microsoft.KeyVault/vaults/keys/update \
--action-group myActionGroup
11.4 Important limitsβ
| Limit | Value |
|---|---|
| Maximum Encryption Scopes per Storage Account | 10,000 |
| Minimum RSA key size for CMK | 2048 bits |
| Supported key types | RSA, RSA-HSM |
| EC (Elliptic Curve) supported? | No for Storage |
12. Integration and Automationβ
12.1 Azure Policy for encryption complianceβ
Ensure all Storage Accounts use CMK:
# Assign built-in policy
az policy assignment create \
--name "storage-cmk-required" \
--policy "6fac406b-40ca-413b-bf8e-0bf964659c25" \
--scope "/subscriptions/<subscription-id>"
The built-in policy Storage accounts should use customer-managed key for encryption audits or denies accounts without CMK.
12.2 Rotation automation with Azure Automationβ
Create a PowerShell Runbook that:
- Lists all Storage Accounts with CMK configured
- Checks the age of the current key version
- Creates new version if the key is older than 90 days
- Updates the Storage Account to the new version
12.3 Integration with Azure Defender for Storageβ
Microsoft Defender for Storage monitors anomalous access patterns to the Storage Account, including attempts to access data with decryption failures, which may indicate wrong key usage or attack.
az security pricing create \
--name StorageAccounts \
--tier Standard
13. Final Summaryβ
Essential concepts:
- Every Storage Account in Azure has AES-256 encryption at rest mandatory active. Cannot be disabled.
- The difference is in who controls the KEK (Key Encryption Key): Microsoft (MMK), the customer via Key Vault (CMK), or the customer via request (CPK).
- The two-layer model (DEK + KEK) allows rotation and access revocation without re-encrypting physical data.
Critical differences:
- MMK: Zero operational effort, zero visibility and control over the key.
- CMK: Complete control and comprehensive auditing, but requires Key Vault with Soft Delete and Purge Protection, Managed Identity and configured permissions.
- CPK: Key never stored in Azure, provided by request. Requires implementation in application code.
- Encryption Scope: Allows different keys per container or blob within the same Storage Account.
- Infrastructure Encryption: Second layer of encryption, enabled only at creation, always managed by Microsoft.
What needs to be remembered:
- Key Vault needs Soft Delete and Purge Protection enabled to be used with CMK.
- The Storage Account accesses Key Vault via Managed Identity with permissions
get,wrapKey,unwrapKey. - Revoking Key Vault access makes the Storage Account immediately inaccessible.
- Infrastructure Encryption can only be enabled at account creation.
- Encryption Scopes cannot be deleted, only disabled (and data becomes inaccessible while disabled).
- Key Vault and Storage Account should be preferably in the same region to minimize latency and failure risk.