CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-azure-mgmt-appconfiguration

Microsoft Azure App Configuration Management Client Library for Python

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

key-values.mddocs/

Key-Value Operations

The KeyValuesOperations class provides direct management of configuration key-value pairs through the Azure Resource Manager API. This enables programmatic CRUD operations on individual configuration keys within App Configuration stores.

Operations Class

class KeyValuesOperations:
    """
    Operations for managing key-value pairs in Azure App Configuration stores.
    
    This class provides methods for creating, reading, updating, and deleting individual
    key-value pairs through the ARM API. Note that this is different from the data plane
    API used by the azure-appconfiguration package.
    """

Key-Value Management

Get Key-Value

def get(
    self, 
    resource_group_name: str, 
    config_store_name: str, 
    key_value_name: str, 
    **kwargs: Any
) -> KeyValue:
    """
    Gets the properties of a key-value pair in an App Configuration store.
    
    Args:
        resource_group_name: The name of the resource group containing the store.
        config_store_name: The name of the configuration store.
        key_value_name: The name of the key-value pair. Use '$' prefix for reserved keys.
        **kwargs: Additional keyword arguments for the request.
        
    Returns:
        KeyValue: The key-value pair with its properties.
        
    Raises:
        HttpResponseError: If the key-value is not found or access is denied.
        
    Example:
        >>> kv = client.key_values.get("my-rg", "my-store", "MyApp:Settings:DatabaseUrl")
        >>> print(f"Value: {kv.value}")
        >>> print(f"Content Type: {kv.content_type}")
        >>> print(f"ETag: {kv.etag}")
        >>> print(f"Last Modified: {kv.last_modified}")
    """

Create or Update Key-Value

def create_or_update(
    self, 
    resource_group_name: str, 
    config_store_name: str, 
    key_value_name: str, 
    key_value_parameters: Optional[KeyValue] = None, 
    **kwargs: Any
) -> KeyValue:
    """
    Creates or updates a key-value pair in an App Configuration store.
    
    Args:
        resource_group_name: The name of the resource group containing the store.
        config_store_name: The name of the configuration store.
        key_value_name: The name of the key-value pair.
        key_value_parameters: The key-value parameters for creation or update.
        **kwargs: Additional keyword arguments for the request.
        
    Returns:
        KeyValue: The created or updated key-value pair.
        
    Note:
        If key_value_parameters is None, creates a key with null value.
        Use ETags in key_value_parameters for conditional updates.
        
    Example:
        >>> from azure.mgmt.appconfiguration.models import KeyValue
        >>> kv_params = KeyValue(
        ...     value="production-database-url",
        ...     content_type="text/plain",
        ...     tags={"Environment": "Production"}
        ... )
        >>> result = client.key_values.create_or_update(
        ...     "my-rg", "my-store", "MyApp:Settings:DatabaseUrl", kv_params
        ... )
        >>> print(f"Created key-value: {result.key}")
    """

Delete Key-Value

def begin_delete(
    self, 
    resource_group_name: str, 
    config_store_name: str, 
    key_value_name: str, 
    **kwargs: Any
) -> LROPoller[None]:
    """
    Deletes a key-value pair from an App Configuration store.
    
    Args:
        resource_group_name: The name of the resource group containing the store.
        config_store_name: The name of the configuration store.
        key_value_name: The name of the key-value pair to delete.
        **kwargs: Additional keyword arguments for the request.
        
    Returns:
        LROPoller[None]: A poller for the long-running delete operation.
        
    Example:
        >>> delete_poller = client.key_values.begin_delete(
        ...     "my-rg", "my-store", "MyApp:Settings:ObsoleteKey"
        ... )
        >>> delete_poller.wait()  # Wait for deletion to complete
        >>> print("Key-value deleted successfully")
    """

Key-Value Models and Properties

KeyValue Model

class KeyValue:
    """
    Represents a key-value pair in Azure App Configuration.
    
    Attributes:
        key (str): The key name (read-only after creation).
        label (str): The label for the key-value pair.
        value (str): The value of the key-value pair.
        content_type (str): The content type of the value.
        etag (str): The ETag for concurrency control (read-only).
        last_modified (datetime): The last modification time (read-only).
        locked (bool): Whether the key-value is locked (read-only).
        tags (Dict[str, str]): Tags associated with the key-value pair.
    """
    
    def __init__(
        self,
        *,
        value: Optional[str] = None,
        content_type: Optional[str] = None,
        tags: Optional[Dict[str, str]] = None,
        **kwargs: Any
    ) -> None:
        """
        Initialize a KeyValue instance.
        
        Args:
            value: The value of the key-value pair.
            content_type: The content type of the value (e.g., "application/json").
            tags: Tags to associate with the key-value pair.
            **kwargs: Additional properties.
        """

Practical Usage Examples

Basic Key-Value Operations

from azure.mgmt.appconfiguration import AppConfigurationManagementClient
from azure.mgmt.appconfiguration.models import KeyValue
from azure.identity import DefaultAzureCredential

# Initialize client
credential = DefaultAzureCredential()
client = AppConfigurationManagementClient(credential, "subscription-id")

resource_group = "my-resource-group"
store_name = "my-config-store"

# Create a simple key-value pair
print("Creating basic key-value...")
basic_kv = KeyValue(value="Hello, World!")
result = client.key_values.create_or_update(
    resource_group, 
    store_name, 
    "MyApp:Greeting",
    basic_kv
)
print(f"Created: {result.key} = {result.value}")

# Create a structured configuration value
print("Creating JSON configuration...")
json_config = KeyValue(
    value='{"timeout": 30, "retries": 3, "endpoint": "https://api.example.com"}',
    content_type="application/json",
    tags={"Type": "Configuration", "Environment": "Production"}
)
result = client.key_values.create_or_update(
    resource_group,
    store_name, 
    "MyApp:ApiSettings",
    json_config
)
print(f"Created JSON config: {result.key}")

# Retrieve a key-value
print("Retrieving key-value...")
retrieved_kv = client.key_values.get(resource_group, store_name, "MyApp:Greeting")
print(f"Retrieved: {retrieved_kv.key} = {retrieved_kv.value}")
print(f"Content-Type: {retrieved_kv.content_type}")
print(f"Last Modified: {retrieved_kv.last_modified}")

Working with Labels

# Key-value pairs can have labels for environment-specific configurations
# Note: Labels are part of the key name in ARM API, formatted as "key$label"

# Production configuration
prod_kv = KeyValue(
    value="prod-database-connection-string",
    content_type="text/plain",
    tags={"Environment": "Production"}
)
client.key_values.create_or_update(
    resource_group,
    store_name,
    "MyApp:Database:ConnectionString$Production",  # Key$Label format
    prod_kv
)

# Development configuration  
dev_kv = KeyValue(
    value="dev-database-connection-string",
    content_type="text/plain",
    tags={"Environment": "Development"}
)
client.key_values.create_or_update(
    resource_group,
    store_name,
    "MyApp:Database:ConnectionString$Development",  # Key$Label format
    dev_kv
)

# Retrieve environment-specific values
prod_config = client.key_values.get(
    resource_group, store_name, "MyApp:Database:ConnectionString$Production"
)
dev_config = client.key_values.get(
    resource_group, store_name, "MyApp:Database:ConnectionString$Development"
)

print(f"Production DB: {prod_config.value}")
print(f"Development DB: {dev_config.value}")

Feature Flag Management

# Create feature flags using reserved key prefix
import json

# Define feature flag structure
feature_flag_value = {
    "id": "BetaFeature",
    "description": "Beta feature for advanced users",
    "enabled": False,
    "conditions": {
        "client_filters": [
            {
                "name": "Microsoft.Percentage",
                "parameters": {
                    "Value": 50
                }
            }
        ]
    }
}

# Create feature flag (use .appconfig.featureflag/ prefix)
feature_flag_kv = KeyValue(
    value=json.dumps(feature_flag_value),
    content_type="application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
    tags={"Type": "FeatureFlag", "Owner": "FeatureTeam"}
)

result = client.key_values.create_or_update(
    resource_group,
    store_name,
    ".appconfig.featureflag/BetaFeature",  # Feature flag key format
    feature_flag_kv
)
print(f"Created feature flag: {result.key}")

# Retrieve and parse feature flag
feature_flag = client.key_values.get(
    resource_group, store_name, ".appconfig.featureflag/BetaFeature"
)
flag_config = json.loads(feature_flag.value)
print(f"Feature '{flag_config['id']}' enabled: {flag_config['enabled']}")

Conditional Updates with ETags

# ETags provide optimistic concurrency control
key_name = "MyApp:Settings:MaxConnections"

# Get current value with ETag
current_kv = client.key_values.get(resource_group, store_name, key_name)
print(f"Current value: {current_kv.value}, ETag: {current_kv.etag}")

# Update with ETag for conditional update
updated_kv = KeyValue(
    value="100",  # New value
    content_type="text/plain",
    # Note: ETags are handled automatically by the service in ARM API
    # For conditional updates, you would typically use If-Match headers in kwargs
)

try:
    # Perform conditional update
    result = client.key_values.create_or_update(
        resource_group,
        store_name,
        key_name,
        updated_kv,
        # For conditional operations, use headers
        headers={"If-Match": current_kv.etag}
    )
    print(f"Updated successfully: {result.value}")
except Exception as e:
    print(f"Conditional update failed: {e}")
    # Handle conflict - value was modified by another client

Bulk Key Management

# Managing multiple keys efficiently
import time

def bulk_create_configuration(config_dict: dict, key_prefix: str = ""):
    """Create multiple configuration keys from a dictionary."""
    results = []
    
    for key, value in config_dict.items():
        full_key = f"{key_prefix}:{key}" if key_prefix else key
        
        # Determine content type based on value type
        if isinstance(value, (dict, list)):
            content_type = "application/json"
            str_value = json.dumps(value)
        elif isinstance(value, bool):
            content_type = "text/plain"
            str_value = str(value).lower()
        elif isinstance(value, (int, float)):
            content_type = "text/plain" 
            str_value = str(value)
        else:
            content_type = "text/plain"
            str_value = str(value)
        
        kv = KeyValue(
            value=str_value,
            content_type=content_type,
            tags={"CreatedBy": "BulkImport", "Timestamp": str(int(time.time()))}
        )
        
        try:
            result = client.key_values.create_or_update(
                resource_group, store_name, full_key, kv
            )
            results.append(result)
            print(f"✓ Created: {result.key}")
        except Exception as e:
            print(f"✗ Failed to create {full_key}: {e}")
    
    return results

# Example configuration
app_config = {
    "Database": {
        "ConnectionString": "Server=prod-db;Database=MyApp;",
        "Timeout": 30,
        "RetryCount": 3
    },
    "Api": {
        "BaseUrl": "https://api.myapp.com",
        "ApiKey": "secret-api-key",
        "RateLimitPerMinute": 1000
    },
    "Features": {
        "EnableCaching": True,
        "EnableLogging": True,
        "DebugMode": False
    }
}

# Create all configurations
print("Creating bulk configuration...")
created_keys = bulk_create_configuration(app_config, "MyApp:Settings")
print(f"Created {len(created_keys)} configuration keys")

Configuration Validation and Cleanup

def validate_and_cleanup_config(prefix: str = ""):
    """Validate configuration keys and clean up invalid ones."""
    print(f"Validating configuration with prefix: {prefix}")
    
    # Note: ARM API doesn't provide list operation for key-values
    # You would need to track keys separately or use the data plane API
    # This example shows how to validate known keys
    
    known_keys = [
        "MyApp:Settings:Database:ConnectionString",
        "MyApp:Settings:Api:BaseUrl", 
        "MyApp:Settings:Features:EnableCaching"
    ]
    
    valid_keys = []
    invalid_keys = []
    
    for key_name in known_keys:
        try:
            kv = client.key_values.get(resource_group, store_name, key_name)
            
            # Validate based on content type and value
            if kv.content_type == "application/json":
                try:
                    json.loads(kv.value)  # Validate JSON
                    valid_keys.append(key_name)
                except json.JSONDecodeError:
                    print(f"✗ Invalid JSON in key: {key_name}")
                    invalid_keys.append(key_name)
            else:
                # Basic validation for non-JSON values
                if kv.value is not None and len(kv.value.strip()) > 0:
                    valid_keys.append(key_name)
                else:
                    print(f"✗ Empty value in key: {key_name}")
                    invalid_keys.append(key_name)
                    
        except Exception as e:
            print(f"✗ Error accessing key {key_name}: {e}")
            invalid_keys.append(key_name)
    
    print(f"Validation complete: {len(valid_keys)} valid, {len(invalid_keys)} invalid")
    
    # Optionally clean up invalid keys
    if invalid_keys:
        response = input(f"Delete {len(invalid_keys)} invalid keys? (y/N): ")
        if response.lower() == 'y':
            for key_name in invalid_keys:
                try:
                    delete_poller = client.key_values.begin_delete(
                        resource_group, store_name, key_name
                    )
                    delete_poller.wait()
                    print(f"✓ Deleted invalid key: {key_name}")
                except Exception as e:
                    print(f"✗ Failed to delete {key_name}: {e}")

# Run validation
validate_and_cleanup_config()

Error Handling

from azure.core.exceptions import HttpResponseError, ResourceNotFoundError

def safe_key_operations():
    """Demonstrate error handling for key-value operations."""
    
    try:
        # Try to get a non-existent key
        kv = client.key_values.get(resource_group, store_name, "NonExistent:Key")
    except ResourceNotFoundError:
        print("Key not found - this is expected for non-existent keys")
    except HttpResponseError as e:
        print(f"HTTP error occurred: {e.status_code} - {e.reason}")
        if hasattr(e, 'error') and e.error:
            print(f"Error details: {e.error}")
    
    try:
        # Try to create a key with invalid characters
        invalid_kv = KeyValue(value="test-value")
        result = client.key_values.create_or_update(
            resource_group, 
            store_name,
            "invalid/key/name",  # Forward slashes may not be allowed
            invalid_kv
        )
    except HttpResponseError as e:
        print(f"Invalid key name error: {e.status_code}")
        # Handle validation errors appropriately
    
    try:
        # Attempt operation on non-existent store
        kv = KeyValue(value="test")
        result = client.key_values.create_or_update(
            resource_group,
            "non-existent-store",
            "TestKey", 
            kv
        )
    except HttpResponseError as e:
        if e.status_code == 404:
            print("Configuration store not found")
        elif e.status_code == 403:
            print("Access denied - check permissions")
        else:
            print(f"Unexpected error: {e.status_code}")

safe_key_operations()

Asynchronous Operations

from azure.mgmt.appconfiguration.aio import AppConfigurationManagementClient
from azure.identity.aio import DefaultAzureCredential
import asyncio

async def async_key_value_operations():
    """Demonstrate asynchronous key-value operations."""
    credential = DefaultAzureCredential()
    
    async with AppConfigurationManagementClient(credential, "subscription-id") as client:
        # Create multiple keys concurrently
        tasks = []
        
        for i in range(5):
            kv = KeyValue(
                value=f"async-value-{i}",
                content_type="text/plain",
                tags={"Batch": "AsyncCreation"}
            )
            
            task = client.key_values.create_or_update(
                resource_group,
                store_name, 
                f"AsyncTest:Key{i}",
                kv
            )
            tasks.append(task)
        
        # Wait for all creates to complete
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        successful_creates = [r for r in results if not isinstance(r, Exception)]
        print(f"Successfully created {len(successful_creates)} keys concurrently")
        
        # Retrieve keys concurrently
        get_tasks = [
            client.key_values.get(resource_group, store_name, f"AsyncTest:Key{i}")
            for i in range(5)
        ]
        
        retrieved_kvs = await asyncio.gather(*get_tasks, return_exceptions=True)
        
        for i, kv in enumerate(retrieved_kvs):
            if not isinstance(kv, Exception):
                print(f"Retrieved: {kv.key} = {kv.value}")

# Run async operations
asyncio.run(async_key_value_operations())

Integration with Azure App Configuration Data Plane

# Note: This ARM API is different from the data plane API
# For comprehensive key-value management, you might use both APIs

from azure.appconfiguration import AzureAppConfigurationClient

def compare_arm_vs_dataplane():
    """Compare ARM API vs Data Plane API for key-value operations."""
    
    # ARM API client (management operations)
    mgmt_client = AppConfigurationManagementClient(credential, subscription_id)
    
    # Data Plane API client (configuration operations)
    # Requires connection string from the store
    store = mgmt_client.configuration_stores.get(resource_group, store_name)
    keys = mgmt_client.configuration_stores.list_keys(resource_group, store_name)
    connection_string = next(iter(keys)).connection_string
    
    data_client = AzureAppConfigurationClient.from_connection_string(connection_string)
    
    print("=== ARM API (Management) ===")
    # ARM API: Individual key operations
    arm_kv = KeyValue(value="arm-api-value", content_type="text/plain")
    arm_result = mgmt_client.key_values.create_or_update(
        resource_group, store_name, "TestKey", arm_kv
    )
    print(f"ARM API created: {arm_result.key}")
    
    print("=== Data Plane API (Configuration) ===")
    # Data Plane API: Rich querying and batch operations
    from azure.appconfiguration import ConfigurationSetting
    
    # Set configuration
    setting = ConfigurationSetting(key="TestKey", value="data-plane-value")
    data_client.set_configuration_setting(setting)
    
    # Query with filters
    settings = data_client.list_configuration_settings(key_filter="Test*")
    for setting in settings:
        print(f"Data Plane found: {setting.key} = {setting.value}")
    
    print("\nUse ARM API for: Store management, access control, metadata")
    print("Use Data Plane API for: Configuration CRUD, queries, real-time updates")

# compare_arm_vs_dataplane()

Install with Tessl CLI

npx tessl i tessl/pypi-azure-mgmt-appconfiguration

docs

configuration-stores.md

index.md

key-values.md

models-and-types.md

operations.md

private-networking.md

replicas.md

snapshots.md

tile.json