Theoretical Foundation: Create and Configure a Container in Azure Blob Storage
1. Initial Intuitionβ
Imagine that a Storage Account is a warehouse. Inside this warehouse, you don't randomly throw boxes on the floor: you organize the space into sections or aisles, each with its own label and access rules. In Azure Blob Storage, these sections are called containers.
A container is the organizational unit within Blob Storage. It groups related blobs (files) and is the level where you define access settings, lifecycle policies, and replication rules.
Every blob in Azure Blob Storage must exist within a container. There are no "loose" blobs in a Storage Account.
2. Contextβ
2.1 Blob Storage Hierarchyβ
2.2 Why containers existβ
Blob Storage is a flat namespace: there's no real directory hierarchy like in a file system. A container is the only real organizational layer. Everything below it (what appears to be subfolders) is actually a prefix in the blob name.
For example, a blob named reports/2025/january/sales.xlsx isn't inside real folders. The entire name is the blob identifier, and the slash / is just a conventional character used to simulate hierarchy.
2.3 What you configure at the container levelβ
The container is the level where you define:
- Public access level (who can access without authentication)
- Stored Access Policies (for SAS tokens)
- Default Encryption Scope (specific encryption key)
- Immutability Policies (WORM: Write Once Read Many)
- Container metadata (custom key-value pairs)
3. Concept Constructionβ
3.1 Blob typesβ
Before understanding containers in depth, you need to know what they store. Azure Blob Storage has three blob types, and each container can contain any combination of them:
Block Blob:
- General-purpose blob for unstructured files
- Divided into blocks up to 4,000 MiB each
- Maximum size: 190.7 TiB
- Uses: images, videos, documents, backups, datasets
Append Blob:
- Optimized for append operations at the end
- Doesn't allow modifying existing blocks
- Maximum size: 195 GiB
- Uses: logs, telemetry data, event streams
Page Blob:
- Organized in 512-byte pages
- Allows efficient random read and write
- Maximum size: 8 TiB
- Uses: unmanaged VM disks (VHDs)
Non-obvious behavior: You don't choose the blob type when creating the container. The type is defined when creating/uploading each blob individually. A single container can have all three types coexisting.
3.2 Container public access levelβ
This is one of the most important and most misunderstood concepts in Blob Storage. There are three levels:
Private (default):
- No anonymous access
- All access requires authentication (key, SAS, Azure AD)
- Recommended for all data that isn't intentionally public
Blob:
- Individual blobs can be accessed anonymously if you know the complete URL
- Cannot list container blobs anonymously
- Useful when you want specific URLs to be public without exposing the inventory
Container:
- Full anonymous access: anyone can list all blobs and access any blob
- Never use for sensitive data
- Useful only for truly public content (public website assets)
| Level | Blob reading with URL | Blob listing | Use |
|---|---|---|---|
| Private | No (requires auth) | No | Default for all data |
| Blob | Yes (known URL) | No | Public download links |
| Container | Yes | Yes | Completely public assets |
Critical security point: Since 2023, Azure added a Storage Account level setting called "Allow Blob Anonymous Access" that controls whether public access can be enabled on any container in the account. If this setting is
false(recommended for production), no container in the account can have public access, regardless of individual container configuration.
3.3 Virtual directories and prefixesβ
As mentioned, Blob Storage doesn't have real directories. What appears to be a folder is a prefix in the blob name:
- Actual blob:
reports/2025/january/sales.xlsx - Container:
data - Complete URL:
https://myaccount.blob.core.windows.net/data/reports/2025/january/sales.xlsx
The listing API allows filtering blobs by prefix using the prefix parameter, which simulates "folder" navigation. Storage Explorer and the portal render these prefixes as folders visually.
When you create a "folder" in the portal or Storage Explorer, what actually happens is that an empty blob (a placeholder) with the name folder/ is created. Some operations remove this placeholder automatically.
3.4 Immutability Policies (WORM)β
Containers can be configured with immutability policies that prevent blob modification or deletion for a defined period. There are two types:
Time-based Retention Policy:
- Defines a retention period (in days) during which blobs cannot be deleted or modified
- After the period, blobs can be deleted normally
- Can be applied at container or individual blob level
Legal Hold:
- Indefinite lock without a defined deadline
- Removed only when the hold is explicitly released
- Used for legal processes and audits
Critical behavior: Once a Time-based Retention Policy is locked, the retention period cannot be reduced, only increased. This is intentional for regulatory compliance (FINRA, SEC, CFTC). Always test in non-production environments before locking.
3.5 Stored Access Policiesβ
A Stored Access Policy is an access policy stored at the container level that can be referenced by multiple SAS tokens. The advantage is that you can revoke or modify access for all SAS tokens referencing a policy by simply updating or deleting the policy, without needing to invalidate each token individually.
4. Structural Viewβ
5. Practical Operationβ
5.1 Container namingβ
The naming rules are strict and failures here cause hard-to-diagnose errors:
- 3 to 63 characters
- Only lowercase letters, numbers, and hyphens
- Must start with letter or number
- Cannot have consecutive hyphens
- The name
$rootis a special reserved container (the "root container") - The name
$webis reserved for static website hosting
5.2 The $web container and static hostingβ
When you enable Static Website on a Storage Account, Azure automatically creates a special container called $web. Files placed in this container are served directly as a static website via a separate endpoint:
https://myaccount.z13.web.core.windows.net
This endpoint is different from the normal blob endpoint. It serves files like a web server, including an index file (index.html) and a customizable 404 error page.
5.3 Leases on containers and blobsβ
A lease is a temporary lock that can be applied to a container or blob. While the lease is active:
- Container lease: Only the lease holder can delete the container
- Blob lease: Only the lease holder can modify or delete the blob
Lease states:
Leases are used by tools like AzCopy and Storage Explorer during transfers to ensure other processes don't modify the blob while the operation is in progress.
5.4 Versioning and Snapshots in container contextβ
When Blob Versioning is enabled on the Storage Account (account-level setting, not container):
- Each blob modification automatically creates a new version
- The current version is the most recent
- Previous versions are read-only
- Versions are stored in the same container as the original blob
When you manually create a Snapshot:
- It's a read-only copy of the blob at a specific moment
- Identified by creation date and time
- Stored in the same container
- URL has the suffix
?snapshot=2025-01-15T10:30:00.0000000Z
6. Implementation Methodsβ
6.1 Azure Portalβ
When to use: One-time creation, exploration, public access configuration, policy application.
In portal: Storage Account > Containers > + Container
You define:
- Container name
- Public access level (if Allow Anonymous Access is enabled on the account)
- Default Encryption Scope (optional)
Limitation: Doesn't support bulk creation or Immutability Policy configuration directly during creation (it's a separate step).
6.2 Azure CLIβ
Creating a container:
# Simple container (private, default)
az storage container create \
--account-name myaccount \
--name images \
--auth-mode login
# Container with public access at blob level
az storage container create \
--account-name myaccount \
--name public-assets \
--public-access blob \
--auth-mode login
# Container with default Encryption Scope
az storage container create \
--account-name myaccount \
--name finance-data \
--default-encryption-scope FinanceScope \
--prevent-encryption-scope-override true \
--auth-mode login
Listing containers:
az storage container list \
--account-name myaccount \
--auth-mode login \
--output table
Configuring metadata:
az storage container metadata update \
--account-name myaccount \
--name images \
--metadata department=marketing environment=production \
--auth-mode login
Applying Stored Access Policy:
az storage container policy create \
--account-name myaccount \
--container-name images \
--name ReadOnlyPolicy \
--permissions r \
--expiry 2025-12-31 \
--auth-mode login
Configuring Time-based Retention Policy:
# Create policy (unlocked state)
az storage container immutability-policy create \
--account-name myaccount \
--resource-group myRG \
--container-name compliance-data \
--period 365
# Lock the policy (irreversible for reduction)
az storage container immutability-policy lock \
--account-name myaccount \
--resource-group myRG \
--container-name compliance-data \
--if-match "<policy-etag>"
6.3 Azure PowerShellβ
# Create context
$ctx = New-AzStorageContext `
-StorageAccountName "myaccount" `
-UseConnectedAccount
# Create private container
New-AzStorageContainer `
-Name "images" `
-Context $ctx
# Create container with public access
New-AzStorageContainer `
-Name "public-assets" `
-Context $ctx `
-Permission Blob
# Upload file to container
Set-AzStorageBlobContent `
-Container "images" `
-File "/local/path/photo.jpg" `
-Blob "products/photo.jpg" `
-Context $ctx `
-StandardBlobTier Hot
# List blobs with prefix (simulate folder navigation)
Get-AzStorageBlob `
-Container "images" `
-Prefix "products/" `
-Context $ctx
6.4 Bicepβ
// Private container
resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${storageAccount.name}/default/images'
properties: {
publicAccess: 'None'
defaultEncryptionScope: 'FinanceScope'
denyEncryptionScopeOverride: true
metadata: {
department: 'marketing'
environment: 'production'
}
}
}
// Container with Immutability Policy (unlocked)
resource complianceContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${storageAccount.name}/default/compliance-data'
properties: {
publicAccess: 'None'
immutableStorageWithVersioning: {
enabled: true
}
}
}
6.5 REST APIβ
For scenarios where neither CLI nor SDK are available:
# Create container via REST API with SAS token
curl -X PUT \
"https://myaccount.blob.core.windows.net/mycontainer?restype=container&<SAS-TOKEN>" \
-H "x-ms-version: 2023-01-03" \
-H "x-ms-blob-public-access: blob" \
-H "Content-Length: 0"
7. Control and Securityβ
7.1 Layered access controlβ
Access to a container and its blobs is determined by a chain evaluation:
7.2 Relevant RBAC roles for containersβ
| Role | What it allows |
|---|---|
| Storage Blob Data Owner | Read, write, delete and ACL control (ADLS Gen2) |
| Storage Blob Data Contributor | Read, write and delete blobs |
| Storage Blob Data Reader | Read-only access to blobs |
| Storage Blob Delegator | Can generate User Delegation SAS tokens |
Important distinction:
Storage Account Contributoris a control plane role (manage the account, its settings). It does not grant access to blob data. For data access,Storage Blob Data *roles are necessary.
7.3 Soft Delete for containers and blobsβ
Blob Soft Delete: When enabled on the Storage Account, deleted blobs remain in "soft-deleted" state for a configurable period (1 to 365 days) before being permanently removed. During this period, they can be restored.
Container Soft Delete: Similar, but for entire containers. A deleted container remains "soft-deleted" and can be restored with all its blobs.
# Enable container soft delete
az storage account blob-service-properties update \
--account-name myaccount \
--resource-group myRG \
--container-delete-retention-days 30 \
--enable-container-delete-retention true
# Enable blob soft delete
az storage account blob-service-properties update \
--account-name myaccount \
--resource-group myRG \
--delete-retention-days 7 \
--enable-delete-retention true
# List soft-deleted containers
az storage container list \
--account-name myaccount \
--include-deleted \
--auth-mode login
# Restore soft-deleted container
az storage container restore \
--account-name myaccount \
--deleted-container-name images \
--deleted-container-version <version-id> \
--auth-mode login
8. Decision Makingβ
8.1 Container public access levelβ
| Situation | Access level | Reason |
|---|---|---|
| Customer, financial, personal data | Private | No sensitive data should be public |
| Public website assets (CSS, JS, images) | Blob or Container | Public access required |
| Temporary download links | Private + SAS | Control with expiration |
| CDN serving content | Blob | CDN authenticates, end user doesn't need to |
| Regulatory compliance | Private + Immutability | Full protection against modification |
| Long-term audit logs | Private + Archive tier | Low cost, rare access |
8.2 Container organizationβ
| Approach | When to use | Trade-off |
|---|---|---|
| One container per data type (images, videos, docs) | Homogeneous data with same configurations | Simple, but no security isolation between data |
| One container per application | Multiple applications in same account | Good isolation per app |
| One container per department | Different teams with sensitive data | Allows granular RBAC per container |
| One container per environment (prod, dev, test) | Different lifecycles and policies | More containers, but distinct policies |
| Single container with prefixes | Huge volume of blobs with same pattern | Lower overhead, but no isolation |
8.3 Immutability: Locked vs Unlockedβ
| Situation | Policy state | Reason |
|---|---|---|
| SEC Rule 17a-4 regulatory compliance | Locked | Regulation requires irreversible immutability |
| Policy testing before production | Unlocked | Allows adjustments and cancellation |
| Legal data in active process | Legal Hold | No defined timeframe, explicit release |
| Backups with guaranteed retention | Locked with defined period | Protection against ransomware |
9. Best Practicesβ
- Disable "Allow Blob Anonymous Access" at Storage Account level in production environments, unless public access is explicitly needed. This prevents any container in the account from being accidentally configured as public.
- Use consistent prefixes to organize blobs within containers:
<year>/<month>/<day>/filefor logs,<category>/<subcategory>/filefor assets. - Enable Soft Delete for containers and blobs with at least 7 days retention in production. The additional cost is minimal compared to the risk of data loss.
- Separate data by lifecycle in different containers. Data that should go to Archive after 90 days fits better in a dedicated container with lifecycle policy targeting that container.
- Use Stored Access Policies instead of ad-hoc SAS tokens when you need multiple tokens with the same permissions. Facilitates centralized revocation.
- Apply metadata to containers with
environment,team,project, andcost-centerto facilitate governance and cost tracking. - For compliance containers, use Immutability Policy in Unlocked state first, test in non-production and only lock when you're absolutely sure of the retention period.
- Never use Storage Account access keys directly in applications. Prefer User Delegation SAS (generated with Azure AD credentials) or RBAC.
10. Common Errorsβ
| Error | Why it happens | How to avoid |
|---|---|---|
| Public container exposes sensitive data | Container access configuration not verified | Audit via Azure Policy: "Storage account public access should be disallowed" |
| Cannot delete container with Immutability | Active policy with non-expired retention period | Check policy state before attempting deletion |
| "Blob not found" in public container | "Blob" level access but attempting to list without authentication | "Blob" level doesn't allow anonymous listing, only URL access |
| Soft delete doesn't restore blobs | Soft delete enabled after blobs were already deleted | Enable soft delete before any critical operation |
| Containers don't appear in portal after deletion | Container soft delete activated, container is in deleted state | Use az storage container list --include-deleted |
| SAS token for container cannot list blobs | Permission l (list) not included in SAS | Include l in SAS permissions when listing is needed |
| Naming error during creation | Uppercase letters or underscores in name | Use only lowercase, numbers and hyphens |
| Lifecycle policy doesn't move blobs to Archive | Container doesn't have Archive tier available (Premium account) | Archive is only available in Standard GPv2 |
11. Operation and Maintenanceβ
11.1 Lifecycle policies (Lifecycle Management)β
Defined at Storage Account level but applied per container via filters:
{
"rules": [
{
"name": "images-lifecycle",
"type": "Lifecycle",
"definition": {
"filters": {
"blobTypes": ["blockBlob"],
"prefixMatch": ["images/"]
},
"actions": {
"baseBlob": {
"tierToCool": { "daysAfterModificationGreaterThan": 30 },
"tierToArchive": { "daysAfterModificationGreaterThan": 180 },
"delete": { "daysAfterModificationGreaterThan": 730 }
},
"snapshot": {
"delete": { "daysAfterCreationGreaterThan": 90 }
}
}
}
}
]
}
Via CLI:
az storage account management-policy create \
--account-name myaccount \
--resource-group myRG \
--policy @lifecycle-policy.json
11.2 Container monitoringβ
Relevant metrics in Azure Monitor:
| Metric | What it indicates |
|---|---|
| BlobCount | Number of blobs per container (filter by ContainerName) |
| BlobCapacity | Capacity used in bytes per container |
| Transactions | Number of operations (may indicate anomalous usage) |
| SuccessE2ELatency | Operation latency |
Configure alerts to detect unexpected growth of BlobCapacity in containers with expected quotas.
11.3 Important limitsβ
| Resource | Limit |
|---|---|
| Containers per Storage Account | Unlimited |
| Blobs per container | Unlimited |
| Maximum size of a Block Blob | 190.7 TiB |
| Maximum size of an Append Blob | 195 GiB |
| Maximum size of a Page Blob | 8 TiB |
| Snapshots per blob | Unlimited (practice: manage lifecycle) |
| Stored Access Policies per container | 5 |
12. Integration and Automationβ
12.1 Event Grid: reacting to container eventsβ
Containers emit events to Azure Event Grid:
Microsoft.Storage.BlobCreatedMicrosoft.Storage.BlobDeletedMicrosoft.Storage.BlobRenamed(ADLS Gen2 only)
This allows building serverless pipelines:
az eventgrid event-subscription create \
--name blob-created-trigger \
--source-resource-id <storage-account-id> \
--endpoint <azure-function-url> \
--included-event-types Microsoft.Storage.BlobCreated \
--subject-begins-with /blobServices/default/containers/images/
The --subject-begins-with filter ensures only events from the images container trigger the function.
12.2 Azure Policy for container governanceβ
Relevant built-in policies:
# Ensure public access is blocked
az policy assignment create \
--name "deny-blob-public-access" \
--policy "d9844e8a-1437-4aeb-a32c-0761373107d4" \
--scope "/subscriptions/<sub-id>"
# Audit containers without soft delete enabled
az policy assignment create \
--name "audit-soft-delete" \
--policy "1a87f16b-6e53-4f10-8d29-9f44d3e2d2b2" \
--scope "/subscriptions/<sub-id>"
12.3 ADLS Gen2: containers as file systemsβ
When the Storage Account has Hierarchical Namespace (HNS) enabled, Blob Storage containers become file systems of Azure Data Lake Storage Gen2:
- Real directories (not just prefixes)
- POSIX permissions per directory and file (ACLs)
- Atomic directory rename operations
- Superior performance for analytics workloads
Nomenclature changes: in ADLS Gen2 context, what would be a container is called a filesystem, but technically it's the same resource.
13. Final Summaryβ
Essential concepts:
- A container is the only real organizational unit in Blob Storage. Every blob exists within a container.
- Blob Storage has a flat namespace: what appears to be folder hierarchy are prefixes in blob names.
- Containers have three public access levels: Private (default), Blob (specific URL), and Container (listing + public read).
- The "Allow Blob Anonymous Access" setting on the account controls whether any container can have public access.
Critical differences:
- Blob vs Container access level: "Blob" level allows URL access but not listing. "Container" level allows both.
- Container vs blob soft delete: These are separate configurations with independent retention periods.
- Storage Account Contributor vs Storage Blob Data Contributor: The first manages the account (control plane). The second accesses data (data plane).
- Immutability Locked vs Unlocked: Unlocked can be cancelled. Locked can only have the period increased, never reduced or removed.
- Stored Access Policy vs direct SAS token: SAP allows centralized revocation. Direct SAS cannot be revoked individually.
What needs to be remembered:
- Container names: 3 to 63 characters, only lowercase, numbers and hyphens.
- Maximum of 5 Stored Access Policies per container.
- Locked Immutability Policy is irreversible for period reduction.
- Soft Delete must be enabled before deleting data; doesn't protect deletions prior to enablement.
- Archive tier is only available in Standard GPv2, not Premium accounts.
- The
$webcontainer is reserved for Static Website hosting. - In ADLS Gen2, containers are called filesystems and support real directories and POSIX ACLs.