Microsoft Azure App Configuration Management Client Library for Python
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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.
"""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}")
"""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}")
"""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")
"""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.
"""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}")# 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}")# 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']}")# 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# 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")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()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()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())# 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