On this page
Common Patterns
Common Patterns
Frequently used patterns and techniques for working with Entra Auth Cli.
Pattern: API Rate Limiting
Handle API rate limiting with exponential backoff.
#!/bin/bash
call_api_with_rate_limit() {
local url=$1
local token=$2
local max_retries=5
local retry=0
while [ $retry -lt $max_retries ]; do
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $token" \
"$url")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" = "200" ]; then
echo "$body"
return 0
elif [ "$http_code" = "429" ]; then
# Rate limited, wait and retry
wait_time=$(( 2 ** retry ))
echo "Rate limited, waiting $wait_time seconds..." >&2
sleep $wait_time
retry=$((retry + 1))
else
echo "Error: HTTP $http_code" >&2
echo "$body" >&2
return 1
fi
done
echo "Max retries exceeded" >&2
return 1
}
# Usage
TOKEN=$(entra-auth-cli get-token -p my-profile --silent)
call_api_with_rate_limit "https://graph.microsoft.com/v1.0/users" "$TOKEN"
Pattern: Parallel API Calls
Make multiple API calls concurrently for better performance.
#!/bin/bash
# Get token once
TOKEN=$(entra-auth-cli get-token -p my-profile --silent)
# Parallel API calls
{
curl -s -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me > user.json &
curl -s -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me/messages > messages.json &
curl -s -H "Authorization: Bearer $TOKEN" \
https://graph.microsoft.com/v1.0/me/calendars > calendars.json &
}
# Wait for all to complete
wait
echo "All API calls completed"
Pattern: Conditional Token Refresh
Only refresh tokens when they’re close to expiration.
#!/bin/bash
get_valid_token() {
local profile=$1
local token_file="/tmp/token-$profile.txt"
local min_validity=300 # 5 minutes
# Check if token exists
if [ -f "$token_file" ]; then
# Check expiration
exp=$(entra-auth-cli inspect -f "$token_file" 2>/dev/null | jq -r .payload.exp)
now=$(date +%s)
remaining=$(( exp - now ))
if [ $remaining -gt $min_validity ]; then
# Token still valid
cat "$token_file"
return 0
fi
fi
# Get fresh token
entra-auth-cli get-token -p "$profile" --silent | tee "$token_file"
chmod 600 "$token_file"
}
# Usage
TOKEN=$(get_valid_token "my-profile")
Pattern: Batch API Requests
Process items in batches to avoid rate limits.
#!/bin/bash
process_batch() {
local items=("$@")
local token=$(entra-auth-cli get-token -p my-profile --silent)
local batch_size=20
for ((i=0; i<${#items[@]}; i+=batch_size)); do
batch=("${items[@]:i:batch_size}")
echo "Processing batch $(( i / batch_size + 1))..."
for item in "${batch[@]}"; do
curl -s -H "Authorization: Bearer $token" \
"https://api.example.com/items/$item" &
done
# Wait for batch to complete
wait
# Rate limit pause between batches
sleep 1
done
}
# Usage
items=(item1 item2 item3 ... item100)
process_batch "${items[@]}"
Pattern: Circuit Breaker
Prevent cascading failures with a circuit breaker pattern.
#!/bin/bash
FAILURE_THRESHOLD=5
TIMEOUT_SECONDS=60
FAILURE_COUNT=0
CIRCUIT_OPEN_UNTIL=0
call_api_with_circuit_breaker() {
local url=$1
local token=$2
local now=$(date +%s)
# Check if circuit is open
if [ $now -lt $CIRCUIT_OPEN_UNTIL ]; then
echo "Circuit breaker open, skipping request" >&2
return 1
fi
# Try API call
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $token" \
"$url")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" = "200" ]; then
# Success: reset failure count
FAILURE_COUNT=0
echo "$body"
return 0
else
# Failure: increment counter
FAILURE_COUNT=$((FAILURE_COUNT + 1))
if [ $FAILURE_COUNT -ge $FAILURE_THRESHOLD ]; then
# Open circuit
CIRCUIT_OPEN_UNTIL=$((now + TIMEOUT_SECONDS))
echo "Circuit breaker opened for $TIMEOUT_SECONDS seconds" >&2
fi
return 1
fi
}
# Usage
TOKEN=$(entra-auth-cli get-token -p my-profile --silent)
call_api_with_circuit_breaker "https://api.example.com/data" "$TOKEN"
Pattern: Request Deduplication
Prevent duplicate requests with request hashing.
#!/bin/bash
declare -A REQUEST_CACHE
call_api_deduplicated() {
local url=$1
local token=$2
local cache_key=$(echo "$url" | md5sum | cut -d' ' -f1)
# Check if request was recently made
if [ -n "${REQUEST_CACHE[$cache_key]}" ]; then
echo "Using cached response for $url" >&2
echo "${REQUEST_CACHE[$cache_key]}"
return 0
fi
# Make request
response=$(curl -s -H "Authorization: Bearer $token" "$url")
# Cache response (with 5 minute TTL)
REQUEST_CACHE[$cache_key]="$response"
# Schedule cache cleanup (background)
(sleep 300; unset REQUEST_CACHE[$cache_key]) &
echo "$response"
}
# Usage
TOKEN=$(entra-auth-cli get-token -p my-profile --silent)
call_api_deduplicated "https://api.example.com/data" "$TOKEN"
call_api_deduplicated "https://api.example.com/data" "$TOKEN" # Uses cache
Pattern: Retry with Jitter
Add randomness to retry delays to avoid thundering herd.
#!/bin/bash
call_api_with_jitter() {
local url=$1
local token=$2
local max_retries=5
local base_delay=1
for retry in $(seq 0 $max_retries); do
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $token" \
"$url")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" = "200" ]; then
echo "$body"
return 0
fi
if [ $retry -lt $max_retries ]; then
# Calculate delay with exponential backoff and jitter
delay=$(( base_delay * (2 ** retry) ))
jitter=$(( RANDOM % delay ))
wait_time=$(( delay + jitter ))
echo "Retry $((retry + 1))/$max_retries, waiting $wait_time seconds..." >&2
sleep $wait_time
fi
done
echo "Max retries exceeded" >&2
return 1
}
# Usage
TOKEN=$(entra-auth-cli get-token -p my-profile --silent)
call_api_with_jitter "https://api.example.com/data" "$TOKEN"
Pattern: Health Check
Implement health checks for monitoring.
#!/bin/bash
health_check() {
local profile=$1
local check_url="https://graph.microsoft.com/v1.0/me"
# Try to get token
if ! token=$(entra-auth-cli get-token -p "$profile" --silent 2>&1); then
echo "FAIL: Could not acquire token"
return 1
fi
# Try to call API
http_code=$(curl -s -w "%{http_code}" -o /dev/null \
-H "Authorization: Bearer $token" \
"$check_url")
if [ "$http_code" = "200" ]; then
echo "OK: Profile $profile is healthy"
return 0
else
echo "FAIL: API call returned HTTP $http_code"
return 1
fi
}
# Usage
if health_check "my-profile"; then
echo "System operational"
else
echo "System degraded"
# Send alert
fi
Pattern: Lazy Initialization
Defer token acquisition until needed.
#!/bin/bash
TOKEN=""
PROFILE="my-profile"
get_token_lazy() {
if [ -z "$TOKEN" ]; then
echo "Acquiring token..." >&2
TOKEN=$(entra-auth-cli get-token -p "$PROFILE" --silent)
fi
echo "$TOKEN"
}
# Token not acquired until first use
echo "Starting script..."
# First API call acquires token
curl -H "Authorization: Bearer $(get_token_lazy)" \
https://api.example.com/data1
# Second call reuses token
curl -H "Authorization: Bearer $(get_token_lazy)" \
https://api.example.com/data2
Pattern: Multi-Tenant Support
Handle multiple Azure tenants in one script.
#!/bin/bash
call_multi_tenant_api() {
local tenant=$1
local endpoint=$2
# Get token for specific tenant
token=$(entra-auth-cli get-token -p "tenant-$tenant" --silent)
# Call API
curl -s -H "Authorization: Bearer $token" "$endpoint"
}
# Usage
tenants=("contoso" "fabrikam" "adventureworks")
for tenant in "${tenants[@]}"; do
echo "Processing tenant: $tenant"
data=$(call_multi_tenant_api "$tenant" "https://graph.microsoft.com/v1.0/users")
echo "$data" | jq
done
Pattern: Graceful Degradation
Provide fallback behavior when authentication fails.
#!/bin/bash
get_data_with_fallback() {
local use_cache=${1:-false}
local cache_file="/tmp/data-cache.json"
# Try to get fresh data
if token=$(entra-auth-cli get-token -p my-profile --silent 2>/dev/null); then
data=$(curl -s -H "Authorization: Bearer $token" \
"https://api.example.com/data")
# Cache successful response
echo "$data" > "$cache_file"
echo "$data"
return 0
fi
# Authentication failed, try cache
if [ -f "$cache_file" ]; then
echo "Using cached data (authentication unavailable)" >&2
cat "$cache_file"
return 0
fi
# No cache available
echo "Data unavailable" >&2
return 1
}
# Usage
get_data_with_fallback