☁️ Azure Key Vault in Shell
Microsoft Azure Key Vault provides secure storage and management of secrets, keys, and certificates for Azure environments. This guide demonstrates how to integrate Azure Key Vault with shell scripts.
🎯 Azure Setup and Authentication
Azure CLI Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # Login to Azure
az login
# Or use service principal for automation
az login --service-principal \
--username "$AZURE_CLIENT_ID" \
--password "$AZURE_CLIENT_SECRET" \
--tenant "$AZURE_TENANT_ID"
# Set subscription
az account set --subscription "your-subscription-id"
# Verify access
az keyvault list
|
Azure Key Vault Health Check
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | # Verify Azure Key Vault connectivity
check_azure_keyvault_health() {
if ! command -v az >/dev/null 2>&1; then
echo "Error: Azure CLI not found" >&2
return 1
fi
# Test connectivity
if az account show >/dev/null 2>&1; then
echo "Azure authentication successful"
else
echo "Error: Azure authentication failed" >&2
return 1
fi
# Test Key Vault access
local test_vault="${AZURE_KEYVAULT_NAME:-}"
if [ -n "$test_vault" ]; then
if az keyvault secret list --vault-name "$test_vault" --query "[0].id" -o tsv >/dev/null 2>&1; then
echo "Azure Key Vault $test_vault is accessible"
return 0
else
echo "Error: Cannot access Azure Key Vault $test_vault" >&2
return 1
fi
else
echo "Warning: AZURE_KEYVAULT_NAME not set, skipping vault test"
return 0
fi
}
|
🔧 Secret Retrieval Patterns
Simple Secret Retrieval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | # Get a simple secret value
get_azure_secret() {
local secret_name="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
if [ -z "$secret_name" ]; then
echo "Error: Secret name required" >&2
return 1
fi
if [ -z "$vault_name" ]; then
echo "Error: Vault name required" >&2
return 1
fi
local secret_value
secret_value=$(az keyvault secret show \
--name "$secret_name" \
--vault-name "$vault_name" \
--query value \
--output tsv 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$secret_value" ]; then
echo "Error: Failed to retrieve secret $secret_name from vault $vault_name" >&2
return 1
fi
echo "$secret_value"
return 0
}
# Usage
DATABASE_PASSWORD=$(get_azure_secret "database-password")
API_KEY=$(get_azure_secret "api-key" "my-keyvault")
|
Secret with Version Management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | # Get specific version of a secret
get_azure_secret_version() {
local secret_name="$1"
local version="$2"
local vault_name="${3:-$AZURE_KEYVAULT_NAME}"
if [ -z "$secret_name" ] || [ -z "$version" ]; then
echo "Error: Secret name and version required" >&2
return 1
fi
local secret_value
secret_value=$(az keyvault secret show \
--name "$secret_name" \
--version "$version" \
--vault-name "$vault_name" \
--query value \
--output tsv 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Failed to retrieve secret $secret_name version $version" >&2
return 1
fi
echo "$secret_value"
return 0
}
# List available versions
list_azure_secret_versions() {
local secret_name="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
az keyvault secret versions \
--name "$secret_name" \
--vault-name "$vault_name" \
--query "[].{Version: id, Enabled: attributes.enabled, Created: attributes.created}" \
--output table
}
|
Certificate Retrieval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | # Get certificate (PEM format)
get_azure_certificate_pem() {
local cert_name="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
local output_file="$3"
if [ -z "$cert_name" ] || [ -z "$vault_name" ]; then
echo "Error: Certificate name and vault name required" >&2
return 1
fi
# Get certificate in PEM format
az keyvault certificate download \
--name "$cert_name" \
--vault-name "$vault_name" \
--file "$output_file" \
--encoding PEM 2>/dev/null
if [ $? -eq 0 ]; then
echo "Certificate downloaded to $output_file"
return 0
else
echo "Error: Failed to download certificate $cert_name" >&2
return 1
fi
}
# Get certificate secret (for private key access)
get_azure_certificate_secret() {
local cert_name="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
# Certificates are stored as secrets with the same name
get_azure_secret "$cert_name" "$vault_name"
}
|
🛡️ Secure Secret Injection
Environment Variable Injection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | # Inject Azure secrets into environment variables
inject_azure_secrets_to_env() {
local secrets_map="$1" # JSON mapping of env_var:secret_name
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
if [ -z "$secrets_map" ]; then
echo "Error: Secrets map required" >&2
return 1
fi
echo "$secrets_map" | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while read -r mapping; do
local env_var="${mapping%=*}"
local secret_name="${mapping#*=}"
local secret_value
secret_value=$(get_azure_secret "$secret_name" "$vault_name")
if [ $? -eq 0 ] && [ -n "$secret_value" ]; then
export "$env_var"="$secret_value"
echo "Exported $env_var from secret $secret_name"
else
echo "Warning: Failed to export $env_var" >&2
fi
done
}
# Usage
SECRETS_MAP='{
"DB_PASSWORD": "database-password",
"API_KEY": "api-key",
"SMTP_PASSWORD": "smtp-password"
}'
inject_azure_secrets_to_env "$SECRETS_MAP"
|
Temporary File Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 | # Create secure config file from Azure secrets
create_secure_config_from_azure_secrets() {
local config_template="$1"
local output_file="$2"
local secrets_mapping="$3"
local vault_name="${4:-$AZURE_KEYVAULT_NAME}"
# Create secure temporary file
local temp_file
temp_file=$(mktemp "${TMPDIR:-/tmp}/azure_config_XXXXXX") || {
echo "Error: Failed to create temporary file" >&2
return 1
}
chmod 600 "$temp_file"
# Build environment for substitution
local env_vars=()
echo "$secrets_mapping" | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while read -r mapping; do
local placeholder="${mapping%=*}"
local secret_name="${mapping#*=}"
local secret_value
secret_value=$(get_azure_secret "$secret_name" "$vault_name")
if [ $? -eq 0 ] && [ -n "$secret_value" ]; then
env_vars+=("$placeholder=$secret_value")
else
echo "Warning: Failed to get secret for $placeholder" >&2
fi
done
# Generate config with substituted values
env "${env_vars[@]}" envsubst < "$config_template" > "$temp_file"
# Move to final location
mv "$temp_file" "$output_file"
chmod 600 "$output_file"
echo "Secure config created: $output_file"
}
# Template file example (app.conf.template):
# database:
# password: ${DB_PASSWORD}
# host: prod-db.vault.azure.net
# api:
# key: ${API_KEY}
# endpoint: https://api.example.com
# Usage
SECRETS_MAPPING='{
"DB_PASSWORD": "database-password",
"API_KEY": "api-key"
}'
create_secure_config_from_azure_secrets "app.conf.template" "/tmp/app.conf" "$SECRETS_MAPPING"
|
🔄 Secret Management and Rotation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # List secrets with metadata
list_azure_secrets_with_metadata() {
local vault_name="${1:-$AZURE_KEYVAULT_NAME}"
local max_results="${2:-25}"
az keyvault secret list \
--vault-name "$vault_name" \
--maxresults "$max_results" \
--query "[].{
Name: name,
ContentType: contentType,
Enabled: attributes.enabled,
Created: attributes.created,
Updated: attributes.updated,
Expires: attributes.expires
}" \
--output table
}
|
Secret Creation and Updates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 | # Create or update a secret
set_azure_secret() {
local secret_name="$1"
local secret_value="$2"
local vault_name="${3:-$AZURE_KEYVAULT_NAME}"
local content_type="${4:-}"
if [ -z "$secret_name" ] || [ -z "$secret_value" ]; then
echo "Error: Secret name and value required" >&2
return 1
fi
local extra_args=()
if [ -n "$content_type" ]; then
extra_args+=(--contentType "$content_type")
fi
az keyvault secret set \
--name "$secret_name" \
--value "$secret_value" \
--vault-name "$vault_name" \
"${extra_args[@]}" \
--output none
if [ $? -eq 0 ]; then
echo "Secret $secret_name set successfully in vault $vault_name"
return 0
else
echo "Error: Failed to set secret $secret_name" >&2
return 1
fi
}
# Delete a secret
delete_azure_secret() {
local secret_name="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
if [ -z "$secret_name" ]; then
echo "Error: Secret name required" >&2
return 1
fi
az keyvault secret delete \
--name "$secret_name" \
--vault-name "$vault_name" \
--output none
if [ $? -eq 0 ]; then
echo "Secret $secret_name deleted from vault $vault_name"
return 0
else
echo "Error: Failed to delete secret $secret_name" >&2
return 1
fi
}
|
Backup and Restore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | # Backup a secret
backup_azure_secret() {
local secret_name="$1"
local backup_file="$2"
local vault_name="${3:-$AZURE_KEYVAULT_NAME}"
if [ -z "$secret_name" ] || [ -z "$backup_file" ]; then
echo "Error: Secret name and backup file required" >&2
return 1
fi
az keyvault secret backup \
--name "$secret_name" \
--vault-name "$vault_name" \
--file "$backup_file"
if [ $? -eq 0 ]; then
echo "Secret $secret_name backed up to $backup_file"
return 0
else
echo "Error: Failed to backup secret $secret_name" >&2
return 1
fi
}
# Restore a secret
restore_azure_secret() {
local backup_file="$1"
local vault_name="${2:-$AZURE_KEYVAULT_NAME}"
if [ -z "$backup_file" ]; then
echo "Error: Backup file required" >&2
return 1
fi
az keyvault secret restore \
--vault-name "$vault_name" \
--file "$backup_file"
if [ $? -eq 0 ]; then
echo "Secret restored from $backup_file to vault $vault_name"
return 0
else
echo "Error: Failed to restore secret from $backup_file" >&2
return 1
fi
}
|
🧪 Azure Key Vault Testing
Mock Azure for Local Testing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 | # Mock Azure Key Vault for local development
mock_azure_keyvault() {
local mock_dir="${1:-/tmp/mock_azure}"
mkdir -p "$mock_dir/secrets"
# Create mock secrets
echo "dbpassword123" > "$mock_dir/secrets/database-password"
echo "apikey123" > "$mock_dir/secrets/api-key"
echo "smtppass123" > "$mock_dir/secrets/smtp-password"
# Create mock Azure CLI script
cat > "$mock_dir/az_mock" << 'EOF'
#!/bin/bash
if [[ "$1" == "keyvault" && "$2" == "secret" && "$3" == "show" ]]; then
# Extract name and vault-name
local secret_name=""
local vault_name=""
for ((i=4; i<=$#; i++)); do
if [[ "${!i}" == "--name" ]]; then
secret_name="${!$((i+1))}"
elif [[ "${!i}" == "--vault-name" ]]; then
vault_name="${!$((i+1))}"
fi
done
if [[ -n "$secret_name" ]]; then
# Convert secret name to file path
local secret_file="/tmp/mock_azure/secrets/$secret_name"
if [[ -f "$secret_file" ]]; then
echo '{"value":"'$(cat "$secret_file")'"}'
else
echo "Error: Secret not found" >&2
exit 1
fi
else
echo "Error: Missing --name" >&2
exit 1
fi
elif [[ "$1" == "account" && "$2" == "show" ]]; then
echo '{"id":"mock-account","user":{"name":"Mock User"}}'
else
echo "Mock Azure: $*" >&2
fi
EOF
chmod +x "$mock_dir/az_mock"
echo "Mock Azure Key Vault created in $mock_dir"
echo "Use: export PATH=\"$mock_dir:\$PATH\" for testing"
}
|
Integration Test Suite
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | # Azure Key Vault integration tests
test_azure_keyvault_integration() {
echo "Testing Azure Key Vault integration..."
# Test connectivity
if ! check_azure_keyvault_health; then
echo "FAIL: Azure Key Vault health check failed"
return 1
fi
# Test secret retrieval
local test_secret
test_secret=$(get_azure_secret "test-secret" 2>/dev/null)
if [ $? -eq 0 ]; then
echo "PASS: Secret retrieval successful"
else
echo "WARN: Could not retrieve test secret (may be expected in CI)"
fi
echo "Azure Key Vault integration tests completed"
}
|
🧾 Summary
✅ Secure authentication using Azure CLI, service principals, or managed identities
✅ Flexible secret retrieval for simple values, versions, and certificates
✅ Safe secret injection via environment variables and temporary files
✅ Complete secret lifecycle management including backup/restore
✅ Comprehensive testing with mock Azure environments
🧾 See Also