env

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 7 Imported by: 2

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:

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

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

func FilterByPrefix(envVars map[string]string, prefix string) map[string]string

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

func FilterByPrefixSlice(envSlice []string, prefix string) []string

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

func GetAzdEnvironmentValues(ctx context.Context, envName string) (map[string]string, error)

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

func LoadAzdEnvironment(ctx context.Context, envName string) error

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

func MapToSlice(env map[string]string) []string

MapToSlice converts an env map into KEY=VALUE entries.

func NormalizeServiceName added in v0.3.0

func NormalizeServiceName(envVarName string) string

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

func ParseKeyValueFormat(output []byte) (map[string]string, error)

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

func SliceToMap(envSlice []string) map[string]string

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.

func (*DefaultCommandRunner) Run added in v0.4.0

func (r *DefaultCommandRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error)

Run executes a command and returns its output.

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

type ResolutionWarning struct {
	Key string
	Err error
}

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

type SecretReferenceChecker interface {
	IsSecretReference(value string) bool
}

SecretReferenceChecker determines whether a value is a secret reference that needs resolution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL