Documentation
¶
Overview ¶
Package env provides environment variable utilities for Azure Developer CLI (azd) extensions.
This package includes:
- Key Vault reference resolution (ResolveMap, ResolveSlice)
- Format conversion (MapToSlice, SliceToMap)
- Pattern-based extraction (FilterByPrefix, ExtractPattern)
- Service name normalization (NormalizeServiceName)
Key Vault Resolution ¶
Use ResolveMap to resolve Azure Key Vault references in environment maps:
import (
"github.com/jongio/azd-core/env"
"github.com/jongio/azd-core/keyvault"
)
resolver, err := keyvault.NewKeyVaultResolver()
if err != nil {
return err
}
envMap := map[string]string{
"DATABASE_URL": "postgres://localhost/db",
"API_KEY": "@Microsoft.KeyVault(VaultName=myvault;SecretName=api-key)",
}
resolved, warnings, err := env.ResolveMap(ctx, envMap, resolver, keyvault.ResolveEnvironmentOptions{})
if err != nil {
return err
}
// resolved["API_KEY"] now contains the actual secret value from Key Vault
Pattern-Based Extraction ¶
Extract environment variables matching specific patterns:
// Filter by prefix (case-insensitive)
azureVars := env.FilterByPrefix(envVars, "AZURE_")
// Returns: {"AZURE_TENANT_ID": "xyz", "AZURE_CLIENT_ID": "abc"}
// Extract SERVICE_*_URL with normalization
serviceURLs := env.ExtractPattern(envVars, env.PatternOptions{
Prefix: "SERVICE_",
Suffix: "_URL",
TrimPrefix: true,
TrimSuffix: true,
Transform: env.NormalizeServiceName,
})
// Returns: {"my-api": "https://...", "web-app": "https://..."}
Service Name Normalization ¶
Convert environment variable naming to service naming conventions:
serviceName := env.NormalizeServiceName("MY_API_SERVICE")
// Returns: "my-api-service"
This is useful for converting uppercase underscore-separated names (common in environment variables) to lowercase hyphen-separated names (common in service identifiers, DNS labels, and container names).
Supported Key Vault Reference Formats ¶
- @Microsoft.KeyVault(SecretUri=https://...)
- @Microsoft.KeyVault(VaultName=...;SecretName=...;SecretVersion=...)
- akvs://<subscription-id>/<vault-name>/<secret-name>[/<version>]
Authentication ¶
Key Vault resolution uses azidentity.DefaultAzureCredential, which supports:
- Environment variables (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET)
- Managed identity (Azure VM, App Service, Container Apps)
- Azure Developer CLI (azd auth login)
- Azure CLI (az login)
- Azure PowerShell
- Interactive browser authentication
Error Handling ¶
By default, resolution continues even if individual references fail (warnings are collected). Use StopOnError to fail fast:
opts := keyvault.ResolveEnvironmentOptions{StopOnError: true}
resolved, warnings, err := env.ResolveMap(ctx, envMap, resolver, opts)
if err != nil {
// First error encountered
}
Package env provides environment variable resolution utilities with Azure Key Vault integration.
This package makes it easy for consumers (like azd-app and azd-rest) to resolve Azure Key Vault references in environment variables. It provides adapter functions to work with both map[string]string and []string representations of environment variables.
Usage with Environment Maps ¶
Use ResolveMap when working with environment maps:
resolver, err := keyvault.NewKeyVaultResolver()
if err != nil {
// handle error
}
envMap := map[string]string{
"DATABASE_URL": "postgres://localhost/db",
"API_KEY": "@Microsoft.KeyVault(VaultName=myvault;SecretName=api-key)",
}
adapter := keyvault.NewEnvResolverAdapter(resolver)
resolved, warnings, err := env.ResolveMap(ctx, envMap, adapter, env.ResolveOptions{})
if err != nil {
// handle error
}
for _, w := range warnings {
// log warning: w.Key, w.Err
}
// resolved["API_KEY"] now contains the actual secret value from Key Vault
Usage with Environment Slices ¶
Use ResolveSlice when working with KEY=VALUE slices (e.g., from os.Environ()):
resolver, err := keyvault.NewKeyVaultResolver()
if err != nil {
// handle error
}
adapter := keyvault.NewEnvResolverAdapter(resolver)
envSlice := os.Environ() // or []string{"KEY=value", ...}
resolved, warnings, err := env.ResolveSlice(ctx, envSlice, adapter, env.ResolveOptions{})
if err != nil {
// handle error
}
// Use resolved with exec.Cmd: cmd.Env = resolved
Error Handling Options ¶
By default, resolution continues even if individual references fail (warnings are collected). Use StopOnError to fail fast:
opts := env.ResolveOptions{StopOnError: true}
resolved, warnings, err := env.ResolveMap(ctx, envMap, adapter, opts)
if err != nil {
// Resolution failed, warnings contains details
}
Supported Key Vault Reference Formats ¶
The package supports three Key Vault reference formats:
- @Microsoft.KeyVault(SecretUri=https://vault.vault.azure.net/secrets/name/version)
- @Microsoft.KeyVault(VaultName=vault;SecretName=name;SecretVersion=version)
- akvs://guid/vault/secret/version
Helper Functions ¶
The package also provides utility functions for working with environment variables:
- MapToSlice: Convert map[string]string to []string (KEY=VALUE format)
- SliceToMap: Convert []string to map[string]string (skips malformed entries)
- HasSecretReferences: Check if any secret references exist
Index ¶
- func ExtractPattern(envVars map[string]string, opts PatternOptions) map[string]string
- func FilterByPrefix(envVars map[string]string, prefix string) map[string]string
- func FilterByPrefixSlice(envSlice []string, prefix string) []string
- func GetAzdEnvironmentValues(ctx context.Context, envName string) (map[string]string, error)
- func HasSecretReferences(envVars []string, checker SecretReferenceChecker) bool
- func LoadAzdEnvironment(ctx context.Context, envName string) error
- func MapToSlice(env map[string]string) []string
- func NormalizeServiceName(envVarName string) string
- func ParseKeyValueFormat(output []byte) (map[string]string, error)
- func SliceToMap(envSlice []string) map[string]string
- type CommandRunner
- type DefaultCommandRunner
- type PatternOptions
- type ResolutionWarning
- func Resolve(ctx context.Context, env map[string]string, resolver Resolver, ...) (map[string]string, []ResolutionWarning, error)
- func ResolveMap(ctx context.Context, envMap map[string]string, resolver Resolver, ...) (map[string]string, []ResolutionWarning, error)
- func ResolveSlice(ctx context.Context, envSlice []string, resolver Resolver, opts ResolveOptions) ([]string, []ResolutionWarning, error)
- type ResolveOptions
- type Resolver
- type SecretReferenceChecker
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractPattern ¶ added in v0.3.0
func ExtractPattern(envVars map[string]string, opts PatternOptions) map[string]string
ExtractPattern extracts environment variables matching prefix/suffix with key transformation. Pattern matching is case-insensitive. Returns a new map with transformed keys.
Example:
envVars := map[string]string{
"SERVICE_API_URL": "https://api.example.com",
"SERVICE_WEB_URL": "https://web.example.com",
"SERVICE_DB_HOST": "db.example.com",
}
// Extract all SERVICE_*_URL variables and normalize service names
urls := env.ExtractPattern(envVars, env.PatternOptions{
Prefix: "SERVICE_",
Suffix: "_URL",
TrimPrefix: true,
TrimSuffix: true,
Transform: func(s string) string { return strings.ToLower(strings.ReplaceAll(s, "_", "-")) },
})
// Returns: {"api": "https://api.example.com", "web": "https://web.example.com"}
func FilterByPrefix ¶ added in v0.3.0
FilterByPrefix returns environment variables matching a prefix. The prefix matching is case-insensitive for keys. Returns a new map containing only the matching entries.
Example:
envVars := map[string]string{
"AZURE_TENANT_ID": "xyz",
"AZURE_CLIENT_ID": "abc",
"DATABASE_URL": "postgres://...",
}
azureVars := env.FilterByPrefix(envVars, "AZURE_")
// Returns: {"AZURE_TENANT_ID": "xyz", "AZURE_CLIENT_ID": "abc"}
func FilterByPrefixSlice ¶ added in v0.3.0
FilterByPrefixSlice returns KEY=VALUE pairs matching a prefix. The prefix matching is case-insensitive for keys. Returns a new slice containing only the matching entries. Malformed entries (without "=") are skipped.
Example:
envSlice := []string{
"AZURE_TENANT_ID=xyz",
"AZURE_CLIENT_ID=abc",
"DATABASE_URL=postgres://...",
}
azureVars := env.FilterByPrefixSlice(envSlice, "AZURE_")
// Returns: ["AZURE_TENANT_ID=xyz", "AZURE_CLIENT_ID=abc"]
func GetAzdEnvironmentValues ¶ added in v0.4.0
GetAzdEnvironmentValues retrieves all environment variables from the specified azd environment without setting them. This is useful when you need the values but don't want to modify the current process environment.
func HasSecretReferences ¶ added in v0.6.0
func HasSecretReferences(envVars []string, checker SecretReferenceChecker) bool
HasSecretReferences quickly checks for any secret-formatted values using the provided checker. This avoids the env package needing to know about specific secret reference formats.
func LoadAzdEnvironment ¶ added in v0.4.0
LoadAzdEnvironment loads all environment variables from the specified azd environment by calling 'azd env get-values' and sets them in the current process.
This ensures that when the -e flag is used, the correct environment values are loaded.
WORKAROUND: This is a workaround for a limitation in the azd extension framework. Ideally, azd should honor the -e flag before invoking the extension and inject the correct environment variables. However, currently azd injects the default environment from config.json, then passes the -e flag to the extension. This forces us to manually reload the environment using 'azd env get-values'.
Do not replace this with azdext.LoadAzdEnvironment. The SDK function runs 'azd env get-values' with no -e flag, so it always reads whichever environment azd considers current. Substituting it would make 'azd <ext> <cmd> -e staging' silently load the default environment instead, which is the exact bug this function exists to prevent. The SDK version also has no JSON path (so values containing newlines break), no environment name validation, and no injectable command runner for tests. See TestSDKLoaderCannotTargetNamedEnvironment.
func MapToSlice ¶
MapToSlice converts an env map into KEY=VALUE entries.
func NormalizeServiceName ¶ added in v0.3.0
NormalizeServiceName converts environment variable naming to service naming. Converts uppercase underscore-separated names to lowercase hyphen-separated names. Commonly used with ExtractPattern to normalize service names from environment variables.
Example:
name := env.NormalizeServiceName("MY_API_SERVICE")
// Returns: "my-api-service"
name = env.NormalizeServiceName("WEB_APP")
// Returns: "web-app"
func ParseKeyValueFormat ¶ added in v0.4.0
ParseKeyValueFormat parses output in "KEY=value" format (one per line). Handles quoted values and skips empty lines and comments.
Do not replace this with azdext.ParseEnvironmentVariables. The SDK parser applies strings.TrimSpace to the value and only strips double quotes, so it changes two kinds of value azd users can legitimately hold: one with significant leading whitespace, and one wrapped in single quotes. Both are pinned by TestParserDivergesFromSDK.
func SliceToMap ¶
SliceToMap converts KEY=VALUE entries into a map, skipping malformed rows.
Types ¶
type CommandRunner ¶ added in v0.4.0
type CommandRunner interface {
Run(ctx context.Context, name string, args ...string) ([]byte, error)
}
CommandRunner is an interface for running external commands. This allows for mocking in tests.
func SetCommandRunner ¶ added in v0.4.0
func SetCommandRunner(runner CommandRunner) CommandRunner
SetCommandRunner sets a custom command runner (useful for testing). Returns the previous runner so it can be restored.
type DefaultCommandRunner ¶ added in v0.4.0
type DefaultCommandRunner struct{}
DefaultCommandRunner uses os/exec to run commands.
type PatternOptions ¶ added in v0.3.0
type PatternOptions struct {
// Prefix is the required prefix for keys (e.g., "SERVICE_")
Prefix string
// Suffix is the optional suffix for keys (e.g., "_URL")
Suffix string
// TrimPrefix removes the prefix from result keys if true
TrimPrefix bool
// TrimSuffix removes the suffix from result keys if true
TrimSuffix bool
// Transform is an optional key transformation function applied after trimming
// Example: func(s string) string { return strings.ToLower(s) }
Transform func(string) string
// Validator is an optional value validation function
// If provided, only entries where Validator(value) returns true are included
// Example: func(v string) bool { return v != "" }
Validator func(string) bool
}
PatternOptions configures pattern-based environment variable extraction.
type ResolutionWarning ¶ added in v0.6.0
ResolutionWarning captures non-fatal resolution failures.
func Resolve ¶
func Resolve(ctx context.Context, env map[string]string, resolver Resolver, opts ResolveOptions) (map[string]string, []ResolutionWarning, error)
Resolve applies a secret resolver to the provided env map if needed.
func ResolveMap ¶
func ResolveMap(ctx context.Context, envMap map[string]string, resolver Resolver, opts ResolveOptions) (map[string]string, []ResolutionWarning, error)
ResolveMap applies the secret resolver to an environment map. It converts the map to a slice, resolves any secret references, and returns a new map with the resolved values. If resolver is nil, the original map is returned (as a copy).
func ResolveSlice ¶
func ResolveSlice(ctx context.Context, envSlice []string, resolver Resolver, opts ResolveOptions) ([]string, []ResolutionWarning, error)
ResolveSlice applies the secret resolver to an environment slice. It takes KEY=VALUE entries, resolves any secret references, and returns a new slice with the resolved values. If resolver is nil, the original slice is returned (as a copy).
type ResolveOptions ¶ added in v0.6.0
type ResolveOptions struct {
StopOnError bool
}
ResolveOptions configures environment resolution behavior.
type Resolver ¶
type Resolver interface {
ResolveEnvironmentVariables(ctx context.Context, env []string, opts ResolveOptions) ([]string, []ResolutionWarning, error)
}
Resolver abstracts secret environment resolution to ease testing and break the dependency on specific secret store packages.
type SecretReferenceChecker ¶ added in v0.6.0
SecretReferenceChecker determines whether a value is a secret reference that needs resolution.