Azure SDK Patterns by Language
Reference for creating skills that teach agents to write code following official Azure SDK guidelines.
Official Documentation: https://azure.github.io/azure-sdk/
Table of Contents
- Core Principles (All Languages)
- Skill Reference Directory Pattern
- Standard Naming Conventions
- Python Patterns
- .NET (C#) Patterns
- Java Patterns
- TypeScript/JavaScript Patterns
- Rust Patterns
- Authentication (All Languages)
- Quick Reference Tables
Core Principles (All Languages)
Azure SDKs follow five design principles. Skills should reinforce these:
| Principle | Meaning |
|---|---|
| Idiomatic | Follow language conventions; feel natural to developers |
| Consistent | APIs feel like a single product from a single team |
| Approachable | Great docs, predictable defaults, progressive disclosure |
| Diagnosable | Clear logging, errors are actionable and human-readable |
| Dependable | No breaking changes without major version bump |
Consistency Priority: Language conventions > Service conventions > Cross-language conventions
Skill Reference Directory Pattern
For Azure SDK skills, keep SKILL.md focused on hero flows and use references/ for overflow details:
references/capabilities.mdis an index only: each hero scenario plus where it is covered (SKILL.mdor a bundled reference), the non-hero scenario list, and links to deep-dive reference files.references/non-hero-scenarios.mdcontains concrete non-hero examples intentionally kept out ofSKILL.md.- Additional
references/*.mdfiles are optional for specialized topics (operation groups, evaluator/tool matrices, migration notes).
Use present-tense guidance in reference files; avoid historical migration notes in user-facing capability indexes.
For Python SDK skills that provide both sync and async clients, present both forms as first-class options with equal priority. Do not encode a blanket preference for either mode in capability prioritization. When the SDK is sync-only or async-only, document the available mode only.
Standard Naming Conventions
Namespace/Package Format
<Azure>.<group>.<service>
| Group | Area | Examples |
|---|---|---|
ai |
AI/ML services | Azure.AI.OpenAI, azure-ai-agents |
data |
Databases | Azure.Data.Cosmos, azure-cosmos |
storage |
Storage services | Azure.Storage.Blobs, @azure/storage-blob |
identity |
Auth/Identity | Azure.Identity, azure-identity |
messaging |
Messaging | Azure.Messaging.ServiceBus |
security |
Security/Crypto | Azure.Security.KeyVault |
Standard Verb Prefixes (All Languages)
| Verb | Behavior | Returns |
|---|---|---|
create |
Create new; fail if exists | Created item |
upsert |
Create or update (database-like) | Item |
set |
Create or update (dictionary-like) | Item |
update |
Fail if doesn't exist | Updated item |
get |
Retrieve single; error if missing | Item |
list |
Return collection (empty if none) | Pageable |
delete |
Succeed even if doesn't exist | void/None |
exists |
Check existence | boolean |
begin |
Start long-running operation | Poller |
Python Patterns
Python Client Naming
# Sync client
class ConfigurationClient:
pass
# Async client - use Async prefix
class AsyncConfigurationClient:
passSync vs Async: Don't Mix Within a Call Path
Rule: Within a single module, script, or code path, use either the sync client or the async client — never both.
- Sync clients live in
azure.<service>(e.g.,azure.ai.projects.AIProjectClient). - Async clients live in
azure.<service>.aio(e.g.,azure.ai.projects.aio.AIProjectClient). - Mixing sync calls inside an
async def(or awaiting inside a sync function) blocks the event loop, breaks context managers, and produces subtle concurrency bugs.
# Setup used by the snippets below
endpoint = "https://example.services.ai.azure.com/api/projects/example"
# ✅ Good — all sync
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
with AIProjectClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client:
agent = client.agents.get_agent("agent-id")
# ✅ Good — all async
from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient
from azure.identity.aio import DefaultAzureCredential as AsyncDefaultAzureCredential
async def run_async():
async with AsyncDefaultAzureCredential() as credential, \
AsyncAIProjectClient(endpoint=endpoint, credential=credential) as client:
agent = await client.agents.get_agent("agent-id")
# ❌ Bad — sync client (azure.ai.projects) called from an async function:
# the synchronous HTTP call blocks the event loop for the entire request.
async def run_bad():
from azure.ai.projects import AIProjectClient # sync client lives in azure.<service>
with AIProjectClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client:
client.agents.get_agent("agent-id") # ← blocking call inside `async def`
# ❌ Bad — async client (azure.<service>.aio) paired with sync DefaultAzureCredential:
# the async client expects an async credential from azure.identity.aio.
async def run_also_bad():
from azure.identity import DefaultAzureCredential # sync
from azure.ai.projects.aio import AIProjectClient # async
async with AIProjectClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client:
await client.agents.get_agent("agent-id") # credential.get_token() will blockWhen writing a skill, present both sync and async forms as first-class options with equal priority when the SDK provides both. Do not encode a preference for either mode. When the SDK is sync-only or async-only, document the available mode only.
Pagination: ItemPaged / AsyncItemPaged
from azure.core.paging import ItemPaged
# Sync iteration
for item in client.list_items():
print(item.name)
# Page-by-page
for page in client.list_items().by_page():
for item in page:
print(item.name)
# With continuation token
for page in client.list_items().by_page(continuation_token="..."):
print(page)
# Async iteration
async for item in async_client.list_items():
print(item.name)Long-Running Operations: LROPoller / AsyncLROPoller
from azure.core.polling import LROPoller
# Start LRO
poller: LROPoller[Result] = client.begin_create_resource(config)
# Check status
if poller.done():
result = poller.result()
# Wait with timeout
result = poller.result(timeout=60)
# Async LRO
async_poller = await async_client.begin_create_resource(config)
result = await async_poller.result()Context Managers (Strongly Preferred)
Always prefer context managers (with / async with) over manually constructing and closing clients. They guarantee the underlying HTTP transport and credential sessions are closed, even on exceptions, and make the sync/async choice explicit at the call site.
# ✅ Preferred — sync
with ConfigurationClient(endpoint, credential) as client:
setting = client.get_setting("key")
# ✅ Preferred — async (also wrap the async credential)
from azure.identity.aio import DefaultAzureCredential
async with DefaultAzureCredential() as credential, \
AsyncConfigurationClient(endpoint, credential) as client:
setting = await client.get_setting("key")
# ⚠️ Only acceptable when the client lifetime spans the whole app
# (e.g., FastAPI lifespan, long-running service). Close it explicitly.
client = ConfigurationClient(endpoint, credential)
try:
setting = client.get_setting("key")
finally:
client.close() # or `await client.close()` for async clientsSkills should show the context-manager form first. Only introduce the explicit close() pattern when the scenario genuinely requires a long-lived client (e.g., dependency-injected singletons), and always pair it with try/finally or a framework lifecycle hook.
Python Error Handling
from azure.core.exceptions import (
ResourceNotFoundError,
ResourceExistsError,
HttpResponseError,
)
try:
item = client.get_item("key")
except ResourceNotFoundError:
print("Not found")
except HttpResponseError as e:
print(f"HTTP {e.status_code}: {e.message}")Docstring Format (Sphinx-style)
def get_setting(self, key: str, **kwargs) -> "ConfigurationSetting":
"""Retrieve a configuration setting.
:param key: The key of the setting.
:type key: str
:keyword timeout: Operation timeout in seconds.
:paramtype timeout: int
:returns: The configuration setting.
:rtype: ~azure.appconfig.ConfigurationSetting
:raises ~azure.core.exceptions.ResourceNotFoundError: If setting not found.
""".NET (C#) Patterns
.NET Client Naming
namespace Azure.Data.Configuration
{
// Service client with Client suffix
public class ConfigurationClient { }
// Options class
public class ConfigurationClientOptions : ClientOptions { }
}.NET Response Wrapper: Response<T>
// Single item
public Response<ConfigurationSetting> GetSetting(string key);
public Task<Response<ConfigurationSetting>> GetSettingAsync(string key);
// No content
public Response DeleteSetting(string key);
public Task<Response> DeleteSettingAsync(string key);.NET Pagination: Pageable<T> / AsyncPageable<T>
// Sync
foreach (ConfigurationSetting setting in client.GetSettings())
{
Console.WriteLine(setting.Key);
}
// Async
await foreach (ConfigurationSetting setting in client.GetSettingsAsync())
{
Console.WriteLine(setting.Key);
}.NET Long-Running Operations: Operation<T>
// With WaitUntil parameter
Operation<AnalyzeResult> operation = await client.StartAnalyzeAsync(
WaitUntil.Completed, // or WaitUntil.Started
document);
AnalyzeResult result = operation.Value;
// Manual polling
Operation<AnalyzeResult> operation = await client.StartAnalyzeAsync(
WaitUntil.Started, document);
while (!operation.HasCompleted)
{
await operation.UpdateStatusAsync();
await Task.Delay(1000);
}Mocking Support
public class ConfigurationClient
{
// Protected parameterless constructor for mocking
protected ConfigurationClient() { }
// Virtual methods for mocking
public virtual Response<ConfigurationSetting> GetSetting(string key);
}.NET Error Handling
try
{
var setting = await client.GetSettingAsync("key");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("Not found");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"Error: {ex.Status} - {ex.ErrorCode}");
}Java Patterns
Java Client Naming
// Sync client
public final class ConfigurationClient { }
// Async client
public final class ConfigurationAsyncClient { }
// Builder (the ONLY way to create clients)
public final class ConfigurationClientBuilder {
public ConfigurationClient buildClient() { }
public ConfigurationAsyncClient buildAsyncClient() { }
}Builder Pattern
ConfigurationClient client = new ConfigurationClientBuilder()
.endpoint(endpoint)
.credential(new DefaultAzureCredentialBuilder().build())
.serviceVersion(ConfigurationServiceVersion.V2023_10_01)
.buildClient();Java Pagination: PagedIterable<T> / PagedFlux<T>
// Sync - standard for loop
for (ConfigurationSetting setting : client.listSettings()) {
System.out.println(setting.getKey());
}
// Sync - Stream API
client.listSettings().stream()
.filter(s -> s.getKey().startsWith("app"))
.forEach(System.out::println);
// Async - Reactor
client.listSettings()
.subscribe(setting -> System.out.println(setting.getKey()));Long-Running Operations: SyncPoller<T,U> / PollerFlux<T,U>
// Sync
SyncPoller<OperationResult, AnalyzeResult> poller =
client.beginAnalyze(document);
poller.waitForCompletion();
AnalyzeResult result = poller.getFinalResult();
// Async
client.beginAnalyze(document)
.last()
.flatMap(AsyncPollResponse::getFinalResult)
.subscribe(result -> System.out.println(result));Reactor Types
| Type | Purpose |
|---|---|
Mono<T> |
0 or 1 item |
Flux<T> |
0 to N items |
PagedFlux<T> |
Paginated collections |
PollerFlux<T,U> |
Long-running operations |
Annotations
@ServiceClient(builder = ConfigurationClientBuilder.class)
public final class ConfigurationClient {
@ServiceMethod(returns = ReturnType.SINGLE)
public ConfigurationSetting getSetting(String key) { }
@ServiceMethod(returns = ReturnType.COLLECTION)
public PagedIterable<ConfigurationSetting> listSettings() { }
}TypeScript/JavaScript Patterns
Package Naming
// Package: @azure/service-name (kebab-case)
// Client: ServiceClient (PascalCase with Client suffix)
import { ServiceClient } from "@azure/service-name";Pagination: PagedAsyncIterableIterator
// Iterate items
for await (const item of client.listItems()) {
console.log(item.name);
}
// Iterate by page
for await (const page of client.listItems().byPage()) {
console.log(`Page has ${page.length} items`);
}
// With continuation token
const iterator = client.listItems().byPage({ continuationToken });Long-Running Operations
// Methods starting LRO use 'begin' prefix
const poller = await client.beginAnalyzeDocument(modelId, document, {
pollInterval: 2000
});
// Wait for completion
const result = await poller.pollUntilDone();
// Serialize state for later
const state = poller.toString();
const restored = await client.beginAnalyzeDocument(modelId, document, {
resumeFrom: state
});Cancellation: AbortSignal
import { AbortController } from "@azure/abort-controller";
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
try {
const item = await client.createItem({
abortSignal: controller.signal
});
} catch (e) {
if (e.name === "AbortError") {
console.log("Cancelled");
}
}Options Pattern
interface CreateItemOptions {
abortSignal?: AbortSignalLike;
timeoutInMs?: number; // Duration suffix: InMs, InSeconds
onlyIfChanged?: boolean; // Conditional request
}TypeScript Error Handling
import { RestError } from "@azure/core-rest-pipeline";
try {
await client.createItem(item);
} catch (e) {
// Check name, not instanceof
if (e.name === "RestError") {
console.error(`HTTP ${e.statusCode}: ${e.message}`);
}
}Rust Patterns
IMPORTANT: Only use the official
azure_*crates published by the azure-sdk crates.io user (e.g.,azure_core,azure_identity,azure_security_keyvault_secrets). Do NOT use the deprecated unofficial crates (azure_sdk_*from MindFlavor/AzureSDKForRust) or the community crates (e.g.,azure_storage, orazure_storage_blobsfrom theazure_sdk_for_rustecosystem). The official crates use underscores in their names and are installed viacargo add. None of the official crates have a version number of 0.21.0. Only create or modify crates usingcargocommands; avoid modifyingCargo.tomlfiles directly if at all possible.Source: All examples below are derived from the official azure-sdk-for-rust repository README files and examples.
Dependency rule: If your Rust code imports
azure_coretypes directly (for example,azure_core::http::Url,azure_core::http::RequestContent, orazure_core::error::ErrorKind), addazure_coretoCargo.toml. If you only use types re-exported by service crates, a directazure_coredependency is optional.
Installation (Rust)
For Rust SDK skills, include the Installation section as:
## Installation
\`\`\`sh
cargo add <crate1> <crate2> <crate3> ...
\`\`\`
> If your code uses \`azure_core\` types directly (for example, \`azure_core::http::Url\` or \`azure_core::http::RequestContent\`), add \`azure_core\` to \`Cargo.toml\`. If you only use types re-exported by service crates, direct \`azure_core\` dependency is optional.Key points:
- Always use
cargo add, never showCargo.tomlmanual edits - List all direct dependencies needed for the examples in the skill
- Include the optional note about
azure_core(copy verbatim) so users understand when to add it explicitly - If examples use
RequestContent::from(), includeazure_corein the install list since that's a directazure_coretype usage
Regenerating Rust SDK Skills from Latest Sources
When a Rust skill appears stale (wrong signatures, outdated examples, deprecated guidance), regenerate it from current upstream sources before editing anything else.
Collect authoritative sources:
- crate README:
sdk/<service>/<crate>/README.md - executable examples:
sdk/<service>/<crate>/examples/*.rs - if needed, public API surface in
src/clients/src/generated
- crate README:
Rebuild skill snippets from those sources:
- prefer README + examples over ad-hoc internet snippets
- align constructor signatures, async patterns, pager/poller usage, and error handling
- keep crate guidance strict: official
azure_*crates published byazure-sdk
Re-validate quality gates:
- run harness scenarios for the affected skill
- run Vally eval if the skill has one (for example,
tests/scenarios/azure-storage-blob-rust/vally/eval.yaml)
Update reference links in the skill to the exact crate docs and source directory used.
Crate Naming
// Crate: azure_<group>_<service> (underscores, all lowercase)
// Client: ServiceClient (PascalCase with Client suffix)
use azure_security_keyvault_secrets::SecretClient;
use azure_security_keyvault_keys::KeyClient;
use azure_security_keyvault_certificates::CertificateClient;
use azure_storage_blob::BlobClient;
use azure_data_cosmos::CosmosClient;
use azure_messaging_eventhubs::ProducerClient;Client Construction
Client construction varies by service. Some use Client::new(), others use builders.
Key Vault: Client::new() function
use azure_identity::DeveloperToolsCredential;
use azure_security_keyvault_secrets::SecretClient;
let credential = DeveloperToolsCredential::new(None)?;
let client = SecretClient::new(
"https://<your-key-vault-name>.vault.azure.net/",
credential.clone(),
None, // Optional SecretClientOptions
)?;
// Get a secret
let secret = client.get_secret("secret-name", None).await?.into_model()?;
println!("Secret: {:?}", secret.value);use azure_core::http::Url;
use azure_identity::DeveloperToolsCredential;
use azure_storage_blob::BlobServiceClient;
let credential = DeveloperToolsCredential::new(None)?;
let service_client = BlobServiceClient::new(service_url, Some(credential), None)?;
let blob_client = service_client.blob_client("<container_name>", "<blob_name>");use azure_identity::DeveloperToolsCredential;
use azure_data_cosmos::{CosmosClient, AccountReference, AccountEndpoint};
let credential = DeveloperToolsCredential::new(None)?;
let endpoint: AccountEndpoint = "https://myaccount.documents.azure.com/".parse()?;
let account = AccountReference::with_credential(endpoint, credential);
let cosmos_client = CosmosClient::builder().build(account).await?;Event Hubs: Builder with open()
use azure_identity::DeveloperToolsCredential;
use azure_messaging_eventhubs::ProducerClient;
let credential = DeveloperToolsCredential::new(None)?;
let producer = ProducerClient::builder()
.open("<EVENTHUBS_HOST>", "<EVENTHUB_NAME>", credential.clone())
.await?;Response Wrapper: Response<T>
// Call a service method returning Response<T>
let response = client.get_secret("secret-name", None).await?;
// Deserialize into a model
let secret = response.into_model()?;
// Or deconstruct for HTTP details
let (status, headers, body) = response.deconstruct();Pagination: Pager<T>
use futures::TryStreamExt;
// Iterate all items across all pages
let mut pager = client.list_secret_properties(None)?;
while let Some(secret) = pager.try_next().await? {
let name = secret.resource_id()?.name;
println!("Found Secret: {}", name);
}
Skills should explicitly document the concrete item yielded by try_next() for the specific SDK being taught. Do not infer the public iteration shape from generated internal model names alone.
- Some Rust Azure clients expose flattened item iteration, where
try_next()already yields the item to process. - Others expose response pages or wrapper models that require an additional loop.
- If the service skill is storage-specific, show the exact public
list_*example from the crate README or examples instead of a generic pager explanation.
The ResourceExt trait provides resource_id() for parsing names and versions from resource IDs:
use azure_security_keyvault_secrets::ResourceExt;
let secret = client.get_secret("my-secret", None).await?.into_model()?;
let id = secret.resource_id()?;
println!("Name: {}, Version: {:?}", id.name, id.version);Long-Running Operations: Poller<T>
LRO methods use the begin_ prefix. The Poller implements IntoFuture — just await it:
use azure_security_keyvault_certificates::models::{
CertificatePolicy, CreateCertificateParameters, IssuerParameters, X509CertificateProperties,
};
let policy = CertificatePolicy {
x509_certificate_properties: Some(X509CertificateProperties {
subject: Some("CN=DefaultPolicy".into()),
..Default::default()
}),
issuer_parameters: Some(IssuerParameters {
name: Some("Self".into()),
..Default::default()
}),
..Default::default()
};
let body = CreateCertificateParameters {
certificate_policy: Some(policy),
..Default::default()
};
// Wait for completion — Poller implements IntoFuture and automatically waits between polls
let certificate = client
.begin_create_certificate("cert-name", body.try_into()?, None)?
.await?
.into_model()?;Rust Error Handling
Key Vault services return structured errors via err.into_inner()?:
match client.get_secret("secret-name", None).await {
Ok(response) => println!("Secret Value: {:?}", response.into_model()?.value),
Err(err) => println!("Error: {:#?}", err.into_inner()?),
}
// Error output includes structured ErrorResponse with code and message:
// ErrorResponse {
// error: ErrorDetails {
// code: Some("SecretNotFound"),
// message: Some("A secret with (name/id) secret-name was not found..."),
// },
// ..
// }Storage client error handling uses StorageError:
use azure_core::error::ErrorKind;
use azure_storage_blob::StorageError;
use azure_storage_blob::models::StorageErrorCode;
match blob_client.download(None).await {
Ok(response) => { /* process response */ }
Err(error) => {
if matches!(error.kind(), ErrorKind::HttpResponse { .. }) {
let storage_error: StorageError = error.try_into()?;
println!("Status: {}", storage_error.status_code);
if let Some(error_code) = &storage_error.error_code {
match error_code {
StorageErrorCode::BlobNotFound => println!("Blob does not exist."),
StorageErrorCode::ContainerNotFound => println!("Container does not exist."),
StorageErrorCode::AuthorizationFailure => println!("Auth failed."),
_ => println!("Other error: {error_code}"),
}
}
}
}
}Note that StorageError::try_into requires an owned error object, it will not compile if handed a reference to an error.
Model Types
// Request/response models: Clone + Default + Serialize/Deserialize
// All non-vector fields are Option<T>
// Response-only models are #[non_exhaustive]
// Use ..Default::default() for struct update syntax
let parameters = UpdateSecretPropertiesParameters {
content_type: Some("text/plain".into()),
tags: Some(HashMap::from_iter(vec![("key".into(), "value".into())])),
..Default::default()
};
// Cosmos DB uses serde for document types
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Item {
pub id: String,
pub partition_key: String,
pub value: String,
}When documenting Rust model types, explicitly teach users how to handle #[non_exhaustive] structs and enums:
- When constructing SDK model structs, always include
..Default::default()even if every currently known field is set. - When matching SDK enums, include a wildcard arm so future service-added variants do not break the match.
- If Clippy or the compiler flags those future-proofing patterns in a minimal example, it is acceptable to locally suppress the warning on that example.
#![allow(dead_code, unused_variables)]
#[derive(Default)]
struct Model {
one: Option<String>,
two: Option<i32>,
}
enum E {
One,
Two,
}
fn main() {
// Future-proof struct construction for non-exhaustive SDK models.
#[allow(clippy::needless_update)]
let model = Model {
one: Some("one".into()),
two: Some(2),
..Default::default()
};
// Future-proof enum matching for non-exhaustive SDK enums.
let value = E::One;
match value {
E::One => println!("One"),
E::Two => println!("Two"),
#[allow(unreachable_patterns)]
_ => panic!("unexpected variant"),
};
}Async Only
The Rust SDK provides only async methods. No sync wrappers:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let client = SecretClient::new(endpoint, credential.clone(), None)?;
let secret = client.get_secret("name", None).await?.into_model()?;
Ok(())
}Key Differences from Other Azure SDKs
| Aspect | Rust | Other Languages |
|---|---|---|
| Auth default | DeveloperToolsCredential |
DefaultAzureCredential |
| Client creation | Client::new() or builder pattern (varies by service) |
Constructors or builders |
| Sync support | Async only (tokio) | Sync + Async |
| Options | Option<ClientOptions> param |
Separate options class |
| Response access | response.into_model()? |
Direct return or .Value |
| LRO prefix | begin_ prefix (e.g., begin_create_certificate) |
begin_ or Begin |
| Debug safety | SafeDebug derive (redacts PII) |
Standard debug |
| Pagination stream | futures::TryStreamExt |
Language iterators |
| Serialization | serde for Cosmos DB documents |
Built-in serializers |
| Thread safety | All clients are Send + Sync; reuse is safe |
Same guarantee |
Rust Skill Authoring Guardrails
When writing or refreshing a Rust Azure SDK skill, include explicit anti-pattern callouts for the mistakes most likely to happen when an agent generalizes from other languages:
- Name the exact credential type to use, and name at least one tempting but invalid credential type if cross-language confusion is likely.
- Show the exact pager item shape for the relevant service client and say whether
try_next()yields items or pages. - Call out optional SDK fields that cannot be printed directly with
{}and show the idiomatic fallback pattern. - If the target scenario or eval expects strict linting, say so explicitly and require
cargo clippy -- -D warningsas a completion gate. - Prefer examples copied from the service crate README or examples directory over reconstructed snippets from generated source.
Authentication (All Languages)
Use the language-idiomatic primary credential pattern:
Python
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
client = ServiceClient(endpoint, credential).NET
var credential = new DefaultAzureCredential();
var client = new ServiceClient(new Uri(endpoint), credential);Java
TokenCredential credential = new DefaultAzureCredentialBuilder().build();
ServiceClient client = new ServiceClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient();TypeScript
import { DefaultAzureCredential } from "@azure/identity";
const credential = new DefaultAzureCredential();
const client = new ServiceClient(endpoint, credential);Rust
use azure_identity::DeveloperToolsCredential;
// Key Vault, Storage: Client::new()
let credential = DeveloperToolsCredential::new(None)?;
let client = SecretClient::new(endpoint, credential.clone(), None)?;
// Cosmos DB: Builder pattern
let account = AccountReference::with_credential(endpoint.parse()?, credential);
let cosmos_client = CosmosClient::builder().build(account).await?;
// Event Hubs: Builder with open()
let producer = ProducerClient::builder()
.open(host, eventhub, credential.clone())
.await?;Important: Rust does not have
DefaultAzureCredential. Skills for Rust should explicitly say “do not useDefaultAzureCredential” when there is any chance of cross-language confusion. UseDeveloperToolsCredentialfor development (tries Azure CLI, then Azure Developer CLI). UseManagedIdentityCredentialfor production on Azure-hosted apps. See Credential structures for the full list.
Rules:
- Never hardcode credentials
- Never persist/cache tokens manually (credential handles refresh)
- Use environment variables for configuration
Quick Reference Tables
Client Types by Language
| Pattern | Python | .NET | Java | TypeScript | Rust |
|---|---|---|---|---|---|
| Sync Client | Client |
Client |
Client |
Client |
N/A (Async only) |
| Async Client | AsyncClient |
N/A (Async methods) | AsyncClient |
N/A (Promise) | Client |
| Builder | N/A | N/A | ClientBuilder |
N/A | new() or builder (varies by service) |
Pagination Types
| Language | Sync | Async |
|---|---|---|
| Python | ItemPaged[T] |
AsyncItemPaged[T] |
| .NET | Pageable<T> |
AsyncPageable<T> |
| Java | PagedIterable<T> |
PagedFlux<T> |
| TypeScript | N/A | PagedAsyncIterableIterator<T> |
| Rust | N/A | Pager<T> (via futures::TryStreamExt) |
LRO Types
| Language | Sync | Async |
|---|---|---|
| Python | LROPoller[T] |
AsyncLROPoller[T] |
| .NET | Operation<T> |
Operation<T> |
| Java | SyncPoller<T,U> |
PollerFlux<T,U> |
| TypeScript | N/A | PollerLike<T> |
| Rust | N/A | Poller<T> (implements IntoFuture + Stream) |
Response Wrappers
| Language | Single Item | Collection |
|---|---|---|
| Python | Direct return | ItemPaged[T] |
| .NET | Response<T> |
Pageable<T> |
| Java | Direct return | PagedIterable<T> |
| TypeScript | Promise<T> |
PagedAsyncIterableIterator<T> |
| Rust | Response<T> |
Pager<T> |
Official Documentation Links
- General Guidelines: https://azure.github.io/azure-sdk/general_introduction.html
- Python: https://azure.github.io/azure-sdk/python_design.html
- .NET: https://azure.github.io/azure-sdk/dotnet_introduction.html
- Java: https://azure.github.io/azure-sdk/java_introduction.html
- TypeScript: https://azure.github.io/azure-sdk/typescript_introduction.html
- Rust: https://azure.github.io/azure-sdk/rust_introduction.html
When creating Azure SDK skills, reference these docs via the microsoft-docs MCP for current API signatures.