Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/appconfiguration/azure-appconfiguration-provider",
"Tag": "python/appconfiguration/azure-appconfiguration-provider_32bd63579a"
"Tag": "python/appconfiguration/azure-appconfiguration-provider_3e69808293"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,110 @@
import asyncio
from azure.appconfiguration.provider.aio import load
from azure.appconfiguration.provider import SettingSelector
from sample_utilities import get_client_modifications
from azure.appconfiguration.aio import AzureAppConfigurationClient # type:ignore
from azure.appconfiguration import ( # type:ignore
ConfigurationSettingsFilter,
ConfigurationSetting,
FeatureFlagConfigurationSetting,
)
from azure.identity.aio import DefaultAzureCredential
import os
import uuid


async def main():
kwargs = get_client_modifications()
connection_string = os.environ["APPCONFIGURATION_CONNECTION_STRING"]
endpoint = os.environ["APPCONFIGURATION_ENDPOINT_STRING"]
credential = DefaultAzureCredential()

# Loading configuration settings from a snapshot
# Note: The snapshot must already exist in your App Configuration store
snapshot_name = "my-snapshot-name"
# Step 1: Create a snapshot
# First, we'll create some configuration settings and then create a snapshot containing them
client = AzureAppConfigurationClient(endpoint, credential)
# Create sample configuration settings (these will be included in the snapshot)
sample_settings = [
ConfigurationSetting(key="app/settings/message", value="Hello from snapshot!"),
ConfigurationSetting(key="app/settings/fontSize", value="14"),
ConfigurationSetting(key="app/settings/backgroundColor", value="#FFFFFF"),
]

# Create a feature flag (also included in the snapshot)
sample_feature_flag = FeatureFlagConfigurationSetting(
feature_id="Beta",
enabled=True,
description="Beta feature flag from snapshot sample",
)

# Override settings with "prod" label (used in mixed selects, not in snapshot)
override_settings = [
ConfigurationSetting(key="override.message", value="Production override!", label="prod"),
ConfigurationSetting(key="override.fontSize", value="16", label="prod"),
]

print("Creating sample configuration settings...")
for setting in sample_settings:
await client.set_configuration_setting(setting)
print(f" Created: {setting.key} = {setting.value}")

# Create the feature flag
await client.set_configuration_setting(sample_feature_flag)
print(f" Created feature flag: {sample_feature_flag.feature_id} = {sample_feature_flag.enabled}")

for setting in override_settings:
await client.set_configuration_setting(setting)
print(f" Created: {setting.key} = {setting.value} (label: {setting.label})")

# Generate a unique snapshot name
snapshot_name = f"sample-snapshot-{uuid.uuid4().hex[:8]}"

# Create snapshot with filters for app settings and feature flags (retention_period=3600 seconds = 1 hour)
snapshot_filters = [
ConfigurationSettingsFilter(key="app/*"),
ConfigurationSettingsFilter(key=".appconfig.featureflag/*"),
]

poller = await client.begin_create_snapshot(name=snapshot_name, filters=snapshot_filters, retention_period=3600)
created_snapshot = await poller.result()
print(f"Created snapshot: {created_snapshot.name} with status: {created_snapshot.status}")

# Step 2: Loading configuration settings from the snapshot
snapshot_selects = [SettingSelector(snapshot_name=snapshot_name)]
config = await load(connection_string=connection_string, selects=snapshot_selects, **kwargs)
config = await load(endpoint=endpoint, credential=credential, selects=snapshot_selects)

print("Configuration settings from snapshot:")
for key, value in config.items():
print(f"{key}: {value}")
await config.close()

# You can also combine snapshot-based selectors with regular selectors
# The snapshot settings and filtered settings will be merged, with later selectors taking precedence
# Step 3: Combine snapshot with regular selectors (later selectors take precedence)
mixed_selects = [
SettingSelector(snapshot_name=snapshot_name), # Load all settings from snapshot
SettingSelector(key_filter="override.*", label_filter="prod"), # Also load specific override settings
]
config_mixed = await load(connection_string=connection_string, selects=mixed_selects, **kwargs)
config_mixed = await load(endpoint=endpoint, credential=credential, selects=mixed_selects)

print("\nMixed configuration (snapshot + filtered settings):")
for key, value in config_mixed.items():
print(f"{key}: {value}")
await config_mixed.close()

# Loading feature flags from a snapshot
# To load feature flags from a snapshot, include the snapshot selector in the 'selects' parameter and set feature_flag_enabled=True.
# Step 4: Load feature flags from the snapshot (requires feature_flag_enabled=True)
feature_flag_selects = [SettingSelector(snapshot_name=snapshot_name)]
config_with_flags = await load(
connection_string=connection_string,
endpoint=endpoint,
credential=credential,
selects=feature_flag_selects,
feature_flag_enabled=True,
**kwargs,
)
print(
f"\nConfiguration includes feature flags: {any(key.startswith('.appconfig.featureflag/') for key in config_with_flags.keys())}"
)

print(f"\nFeature flags loaded: {'feature_management' in config_with_flags}")
if "feature_management" in config_with_flags:
feature_flags = config_with_flags["feature_management"].get("feature_flags", [])
for flag in feature_flags:
print(f" {flag['id']}: enabled={flag['enabled']}")

await client.close()
await config_with_flags.close()
await credential.close()


if __name__ == "__main__":
asyncio.run(main())
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,104 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# -------------------------------------------------------------------------

from azure.appconfiguration.provider import load, SettingSelector
from sample_utilities import get_authority, get_credential, get_client_modifications
import os
import uuid
from azure.identity import DefaultAzureCredential
from azure.appconfiguration.provider import load, SettingSelector
from azure.appconfiguration import ( # type:ignore
AzureAppConfigurationClient,
ConfigurationSettingsFilter,
ConfigurationSnapshot,
ConfigurationSetting,
FeatureFlagConfigurationSetting,
)

endpoint = os.environ["APPCONFIGURATION_ENDPOINT_STRING"]
credential = DefaultAzureCredential()

# Step 1: Create a snapshot
# First, we'll create some configuration settings and then create a snapshot containing them
client = AzureAppConfigurationClient(endpoint, credential)


# Create sample configuration settings (these will be included in the snapshot)
sample_settings = [
ConfigurationSetting(key="app/settings/message", value="Hello from snapshot!"),
ConfigurationSetting(key="app/settings/fontSize", value="14"),
ConfigurationSetting(key="app/settings/backgroundColor", value="#FFFFFF"),
]

# Create a feature flag (also included in the snapshot)
sample_feature_flag = FeatureFlagConfigurationSetting(
feature_id="Beta",
enabled=True,
description="Beta feature flag from snapshot sample",
)

# Override settings with "prod" label (used in mixed selects, not in snapshot)
override_settings = [
ConfigurationSetting(key="override.message", value="Production override!", label="prod"),
ConfigurationSetting(key="override.fontSize", value="16", label="prod"),
]

print("Creating sample configuration settings...")
for setting in sample_settings:
client.set_configuration_setting(setting)
print(f" Created: {setting.key} = {setting.value}")

# Create the feature flag
client.set_configuration_setting(sample_feature_flag)
print(f" Created feature flag: {sample_feature_flag.feature_id} = {sample_feature_flag.enabled}")

endpoint = os.environ.get("APPCONFIGURATION_ENDPOINT_STRING")
authority = get_authority(endpoint)
credential = get_credential(authority)
kwargs = get_client_modifications()
for setting in override_settings:
client.set_configuration_setting(setting)
print(f" Created: {setting.key} = {setting.value} (label: {setting.label})")

# Connecting to Azure App Configuration using AAD
config = load(endpoint=endpoint, credential=credential, **kwargs)
# Generate a unique snapshot name
snapshot_name = f"sample-snapshot-{uuid.uuid4().hex[:8]}"

# Loading configuration settings from a snapshot
# Note: The snapshot must already exist in your App Configuration store
snapshot_name = "my-snapshot-name"
# Create snapshot with filters for app settings and feature flags (retention_period=3600 seconds = 1 hour)
snapshot_filters = [
ConfigurationSettingsFilter(key="app/*"),
ConfigurationSettingsFilter(key=".appconfig.featureflag/*"),
]

created_snapshot = client.begin_create_snapshot(
name=snapshot_name, filters=snapshot_filters, retention_period=3600
).result()
print(f"Created snapshot: {created_snapshot.name} with status: {created_snapshot.status}")


# Step 2: Loading configuration settings from the snapshot
snapshot_selects = [SettingSelector(snapshot_name=snapshot_name)]
config = load(endpoint=endpoint, credential=credential, selects=snapshot_selects, **kwargs)
config = load(endpoint=endpoint, credential=credential, selects=snapshot_selects)

print("Configuration settings from snapshot:")
for key, value in config.items():
print(f"{key}: {value}")

# You can also combine snapshot-based selectors with regular selectors
# The snapshot settings and filtered settings will be merged, with later selectors taking precedence
# Step 3: Combine snapshot with regular selectors (later selectors take precedence)
mixed_selects = [
SettingSelector(snapshot_name=snapshot_name), # Load all settings from snapshot
SettingSelector(key_filter="override.*", label_filter="prod"), # Also load specific override settings
]
config_mixed = load(endpoint=endpoint, credential=credential, selects=mixed_selects, **kwargs)
config_mixed = load(endpoint=endpoint, credential=credential, selects=mixed_selects)

print("\nMixed configuration (snapshot + filtered settings):")
for key, value in config_mixed.items():
print(f"{key}: {value}")

# Loading feature flags from a snapshot
# To load feature flags from a snapshot, include the snapshot selector in the `selects` parameter and set `feature_flag_enabled=True`.
# Step 4: Load feature flags from the snapshot (requires feature_flag_enabled=True)
feature_flag_selects = [SettingSelector(snapshot_name=snapshot_name)]
config_with_flags = load(
endpoint=endpoint,
credential=credential,
selects=feature_flag_selects,
feature_flag_enabled=True,
**kwargs,
)

print(
f"\nConfiguration includes feature flags: {any(key.startswith('.appconfig.featureflag/') for key in config_with_flags.keys())}"
)
print(f"\nFeature flags loaded: {'feature_management' in config_with_flags}")
if "feature_management" in config_with_flags:
feature_flags = config_with_flags["feature_management"].get("feature_flags", [])
for flag in feature_flags:
print(f" {flag['id']}: enabled={flag['enabled']}")
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ async def test_snapshot_selector_parameter_validation_in_provider(self, appconfi
feature_flag_selectors=[SettingSelector(snapshot_name="test-snapshot")],
)

@pytest.mark.live_test_only # Needed to fix an azure core dependency compatibility issue
@app_config_decorator_async
@recorded_by_proxy_async
async def test_create_snapshot_and_load_provider(self, appconfiguration_connection_string, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def test_snapshot_selector_parameter_validation_in_provider(self, appconfigurati
feature_flag_selectors=[SettingSelector(snapshot_name="test-snapshot")],
)

@pytest.mark.live_test_only # Needed to fix an azure core dependency compatibility issue
@app_config_decorator
@recorded_by_proxy
def test_create_snapshot_and_load_provider(self, appconfiguration_connection_string, **kwargs):
Expand Down
14 changes: 13 additions & 1 deletion sdk/appconfiguration/azure-appconfiguration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,26 @@ client = AzureAppConfigurationClient.from_connection_string(CONNECTION_STRING)

<!-- END SNIPPET -->

#### Use AAD token
#### Use Entra ID token

Here we demonstrate using [DefaultAzureCredential][default_cred_ref]
to authenticate as a service principal. However, [AzureAppConfigurationClient][configuration_client_class]
accepts any [azure-identity][azure_identity] credential. See the
[azure-identity][azure_identity] documentation for more information about other
credentials.

<!-- SNIPPET:hello_world_entra_id_sample.create_app_config_client -->

```python

ENDPOINT = os.environ["APPCONFIGURATION_ENDPOINT"]
credential = DefaultAzureCredential()
# Create app config client
client = AzureAppConfigurationClient(base_url=ENDPOINT, credential=credential)
```

<!-- END SNIPPET -->

##### Create a service principal (optional)
This [Azure CLI][azure_cli] snippet shows how to create a
new service principal. Before using it, replace "your-application-name" with
Expand Down
2 changes: 1 addition & 1 deletion sdk/appconfiguration/azure-appconfiguration/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/appconfiguration/azure-appconfiguration",
"Tag": "python/appconfiguration/azure-appconfiguration_7b8ff3a790"
"Tag": "python/appconfiguration/azure-appconfiguration_e031d16e39"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# coding: utf-8

# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

"""
FILE: hello_world_entra_id_sample.py

DESCRIPTION:
This sample demos how to add/update/retrieve/delete configuration settings synchronously.

USAGE: python hello_world_entra_id_sample.py

Set the environment variables with your own values before running the sample:
1) APPCONFIGURATION_CONNECTION_STRING: Connection String used to access the Azure App Configuration.
"""
import os
from azure.appconfiguration import AzureAppConfigurationClient
from azure.identity import DefaultAzureCredential
from azure.appconfiguration import ConfigurationSetting


def main():
# [START create_app_config_client]

ENDPOINT = os.environ["APPCONFIGURATION_ENDPOINT"]
credential = DefaultAzureCredential()
# Create app config client
client = AzureAppConfigurationClient(base_url=ENDPOINT, credential=credential)
# [END create_app_config_client]

print("Add new configuration setting")
# [START create_config_setting]
config_setting = ConfigurationSetting(
key="MyKey", label="MyLabel", value="my value", content_type="my content type", tags={"my tag": "my tag value"}
)
added_config_setting = client.add_configuration_setting(config_setting)
# [END create_config_setting]
print("New configuration setting:")
print(added_config_setting)
print("")

print("Set configuration setting")
# [START set_config_setting]
added_config_setting.value = "new value"
added_config_setting.content_type = "new content type"
updated_config_setting = client.set_configuration_setting(added_config_setting)
# [END set_config_setting]
print(updated_config_setting)
print("")

print("Get configuration setting")
# [START get_config_setting]
fetched_config_setting = client.get_configuration_setting(key="MyKey", label="MyLabel")
# [END get_config_setting]
print("Fetched configuration setting:")
print(fetched_config_setting)
print("")

print("Delete configuration setting")
# [START delete_config_setting]
client.delete_configuration_setting(key="MyKey", label="MyLabel")
# [END delete_config_setting]


if __name__ == "__main__":
main()
Loading