Azure Blob Storage library for Rust
Client library for Azure Blob Storage — upload, download, and manage blobs and containers.
Use this skill when:
- An app needs to upload or download blobs from Azure Storage in Rust
- You need to create or manage blob containers
- You need to list blobs with pagination
- You need RBAC-based auth for blob operations
IMPORTANT: Only use the official
azure_storage_blobcrate published by the azure-sdk crates.io user. Do NOT use the unofficialazure_storage,azure_storage_blobs, orazure_sdk_for_rustcommunity crates. Official crates use underscores in names and none have version 0.21.0.
Installation
cargo add azure_storage_blob azure_identity azure_core tokio futures
If your code uses
azure_coretypes directly (for example,azure_core::http::Urlorazure_core::http::RequestContent), addazure_coretoCargo.toml. If you only useazure_storage_blobre-exports, directazure_coredependency is optional.
Environment Variables
AZURE_STORAGE_ACCOUNT=<account-name> # Preferred when the caller gives an account name
AZURE_STORAGE_ENDPOINT=https://<account>.blob.core.windows.net/ # Optional alternative when the caller gives a full endpoint
When both are available, prefer constructing the endpoint from AZURE_STORAGE_ACCOUNT so the code matches common evaluation prompts.
Authentication
Rust Azure SDK code must not use DefaultAzureCredential. The Rust identity crate does not provide that type.
// Correct for local development
use azure_identity::DeveloperToolsCredential;
let credential = DeveloperToolsCredential::new(None)?;
// Incorrect in Rust: this type does not exist in azure_identity
use azure_identity::DefaultAzureCredential;
use azure_core::http::Url;
use azure_identity::DeveloperToolsCredential;
use azure_storage_blob::BlobServiceClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Local dev: DeveloperToolsCredential. Production: use ManagedIdentityCredential.
let credential = DeveloperToolsCredential::new(None)?;
let service_url = Url::parse("https://<storage_account_name>.blob.core.windows.net/")?;
let service_client = BlobServiceClient::new(
service_url,
Some(credential),
None,
)?;
// Derive container and blob clients by name.
let container_client = service_client.blob_container_client("<container_name>");
let blob_client = container_client.blob_client("<blob_name>");
Ok(())
}
Client Types
| Client | Purpose |
|---|---|
BlobServiceClient |
Account-level operations, list containers |
BlobContainerClient |
Container operations, list blobs |
BlobClient |
Individual blob operations |
Core Workflow
Upload Blob
use azure_core::http::{RequestContent, Url};
use azure_identity::DeveloperToolsCredential;
use azure_storage_blob::BlobServiceClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Local dev: DeveloperToolsCredential. Production: use ManagedIdentityCredential.
let credential = DeveloperToolsCredential::new(None)?;
let service_url = Url::parse("https://<storage_account_name>.blob.core.windows.net/")?;
let service_client = BlobServiceClient::new(service_url, Some(credential), None)?;
let blob_client = service_client.blob_client("<container_name>", "<blob_name>");
let data = b"hello world";
blob_client.upload(RequestContent::from(data.to_vec()), None).await?;
Ok(())
}
Download Blob / Get Properties
// Get blob properties
let props = blob_client.get_properties(None).await?;
// Download blob content
let response = blob_client.download(None).await?;
let content = String::from_utf8(response.body.collect().await?.into())?;
Delete Blob
blob_client.delete(None).await?;
Container Operations
use azure_core::http::Url;
use azure_identity::DeveloperToolsCredential;
use azure_storage_blob::BlobServiceClient;
use futures::TryStreamExt as _;
let credential = DeveloperToolsCredential::new(None)?;
let service_url = Url::parse("https://<storage_account_name>.blob.core.windows.net/")?;
let service_client = BlobServiceClient::new(service_url, Some(credential), None)?;
let container_client = service_client.blob_container_client("<container_name>");
// Create container
container_client.create(None).await?;
// List blobs (the pager yields BlobItem values directly for this client pattern)
let mut pager = container_client.list_blobs(None)?;
while let Some(blob) = pager.try_next().await? {
let name = blob.name.as_deref().unwrap_or("<unnamed>");
let size = blob
.properties
.as_ref()
.and_then(|properties| properties.content_length);
match size {
Some(size) => println!("Blob: {name} ({size} bytes)"),
None => println!("Blob: {name}"),
}
}
For azure_storage_blob 1.x, do not assume you need a nested page loop like for item in &page.blob_items. In this usage pattern, try_next() already yields the blob item you want to print.
Error Handling
Use StorageError for programmatic access to storage-specific error codes:
use azure_core::error::ErrorKind;
use azure_storage_blob::StorageError;
use azure_storage_blob::models::StorageErrorCode;
let result = blob_client.download(None).await;
match result {
Ok(response) => {
let content: Vec<u8> = response.body.collect().await?.into();
println!("Downloaded {} bytes", content.len());
}
Err(error) => {
if matches!(error.kind(), ErrorKind::HttpResponse { .. }) {
// Convert to StorageError for programmatic access
let storage_error: StorageError = error.try_into()?;
println!("HTTP Status: {}", storage_error.status_code);
if let Some(error_code) = &storage_error.error_code {
match error_code {
StorageErrorCode::BlobNotFound => {
println!("The blob does not exist.");
}
StorageErrorCode::ContainerNotFound => {
println!("The container does not exist.");
}
StorageErrorCode::AuthorizationFailure => {
println!("Authorization failed. Check RBAC roles.");
}
_ => println!("Storage error: {error_code}"),
}
}
if let Some(request_id) = &storage_error.request_id {
println!("Request ID (for Azure support): {request_id}");
}
} else {
println!("Non-HTTP error: {:?}", error);
}
}
}
Note:
StorageError::try_intorequires an owned error object — it will not compile if handed a reference to an error.
RBAC Roles
For Entra ID auth, assign one of these roles to the identity:
| Role | Access |
|---|---|
Storage Blob Data Reader |
Read-only |
Storage Blob Data Contributor |
Read/write |
Storage Blob Data Owner |
Full access including RBAC |
Best Practices
- Use
cargo addto manage dependencies, never editCargo.tomldirectly. Add and remove Rust SDK dependencies with cargo commands instead of manual manifest edits. - Add
azure_coreonly when importingazure_coretypes directly. If your code importsazure_core::http::Url,azure_core::http::RequestContent, orazure_core::error::ErrorKind, includeazure_core; otherwise a direct dependency is optional. - Use
DeveloperToolsCredentialfor local dev,ManagedIdentityCredentialfor production — Rust does not provide a singleDefaultAzureCredentialtype - Never hardcode credentials — use environment variables or managed identity
- Use
RequestContent::from()to wrap data for blob uploads — ensures proper content handling by the SDK - Assign RBAC roles — ensure "Storage Blob Data Contributor" for write access
- Reuse clients — clients are thread-safe; create once, share across tasks
- Prefer
BlobServiceClientas the entry point and derive container/blob clients from it - Treat many storage model fields as optional.
blob.nameis anOption<String>and content length is accessed viablob.properties.as_ref().and_then(|p| p.content_length). - Run
cargo clippy -- -D warningsbefore considering the task complete when the prompt or CI expects strict lint compliance; fix style lints such as collapsibleifblocks, not just compiler errors. - Prefer crate README/examples over generated internal type names when validating public API shapes such as pagination results
- Future-proof
#[non_exhaustive]SDK models — when constructing SDK model/options structs, end the initializer with..Default::default()(add#[allow(clippy::needless_update)]) and use a_wildcard arm when matching SDK enums, so new service-added fields/variants don't break your build
Common Rust Blob Pitfalls
- Do not import
azure_identity::DefaultAzureCredential; useDeveloperToolsCredentialor another real Rust credential type. - Do not assume generated internal model names describe the public pager item type; follow the documented
list_blobsexample for this crate. - Do not print
blob.namewith{}directly; unwrap or provide a fallback because it is optional. - Do not stop after
cargo buildpasses when the task also requirescargo clippy -- -D warnings.
Reference Links
| Resource | Link |
|---|---|
| API Reference | https://docs.rs/crate/azure_storage_blob/latest |
| crates.io | https://crates.io/crates/azure_storage_blob |
| Source Code | https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob |