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
12 changes: 12 additions & 0 deletions src/azure-cli/azure/cli/command_modules/appconfig/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
text: az appconfig create -g MyResourceGroup -n MyAppConfiguration -l westus --sku Standard --kv-revision-retention-period 86400
- name: Create an App Configuration store linked to an Azure Front Door profile.
text: az appconfig create -g MyResourceGroup -n MyAppConfiguration -l westus --sku Standard --azure-front-door-profile /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCEGROUP>/providers/Microsoft.Cdn/profiles/<PROFILE_NAME>
- name: Create an App Configuration store with an Application Insights resource linked for telemetry collection.
text: az appconfig create -g MyResourceGroup -n MyAppConfiguration -l westus --sku Standard --appinsights-resource /subscriptions/<SUBSCRIPTIONID>/resourceGroups/<RESOURCEGROUP>/providers/microsoft.insights/components/MyAppInsights
"""

helps['appconfig list-deleted'] = """
Expand Down Expand Up @@ -420,6 +422,10 @@
text: az appconfig update -g MyResourceGroup -n MyAppConfiguration --azure-front-door-profile /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCEGROUP>/providers/Microsoft.Cdn/profiles/<PROFILE_NAME>
- name: Update an App Configuration store to unlink an Azure Front Door profile.
text: az appconfig update -g MyResourceGroup -n MyAppConfiguration --azure-front-door-profile ""
- name: Link an Application Insights resource to an App Configuration store for telemetry collection.
text: az appconfig update -g MyResourceGroup -n MyAppConfiguration --appinsights-resource /subscriptions/<SUBSCRIPTIONID>/resourceGroups/<RESOURCEGROUP>/providers/microsoft.insights/components/MyAppInsights
- name: Unlink Application Insights from an App Configuration store.
text: az appconfig update -g MyResourceGroup -n MyAppConfiguration --appinsights-resource ""
"""

helps['appconfig feature'] = """
Expand All @@ -446,6 +452,12 @@
- name: Set a feature flag with name "Beta" and custom key ".appconfig.featureflag/MyApp1:Beta" with tags "tag1=value1" and "tag2=value2".
text:
az appconfig feature set -n MyAppConfiguration --feature Beta --key .appconfig.featureflag/MyApp1:Beta --tags tag1=value1 tag2=value2
- name: Set a feature flag with telemetry enabled.
text:
az appconfig feature set -n MyAppConfiguration --feature color --telemetry-enabled
- name: Disable telemetry on a feature flag.
text:
az appconfig feature set -n MyAppConfiguration --feature color --telemetry-enabled false
"""

helps['appconfig feature delete'] = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def load_arguments(self, _):
c.argument('no_replica', help='Proceed without replica creation for premium tier store.', arg_type=get_three_state_flag())
c.argument('arm_auth_mode', arg_type=arm_auth_mode_arg_type)
c.argument('enable_arm_private_network_access', arg_type=enable_arm_private_network_access_arg_type)
c.argument('appinsights_resource', help='Resource ID of the Application Insights resource to link with this App Configuration store.')
c.argument('kv_revision_retention_period', arg_type=kv_revision_retention_period_arg_type)
c.argument('azure_front_door_profile', help='Resource ID of an Azure Front Door profile to link to this App Configuration store.', is_preview=True)

Expand All @@ -201,6 +202,7 @@ def load_arguments(self, _):
c.argument('enable_purge_protection', options_list=['--enable-purge-protection', '-p'], arg_type=get_three_state_flag(), help='Property specifying whether protection against purge is enabled for this App Configuration store. Setting this property to true activates protection against purge for this App Configuration store and its contents. Enabling this functionality is irreversible.')
c.argument('arm_auth_mode', arg_type=arm_auth_mode_arg_type)
c.argument('enable_arm_private_network_access', arg_type=enable_arm_private_network_access_arg_type)
c.argument('appinsights_resource', help='Resource ID of the Application Insights resource to link with this App Configuration store.')
c.argument('kv_revision_retention_period', arg_type=kv_revision_retention_period_arg_type)
c.argument('azure_front_door_profile', help='Resource ID of an Azure Front Door profile to link to this App Configuration store. Pass "" to unlink a Front Door profile.', is_preview=True)

Expand Down Expand Up @@ -376,6 +378,7 @@ def load_arguments(self, _):
c.argument('key', validator=validate_feature_key, help='Key of the feature flag. Key must start with the ".appconfig.featureflag/" prefix. Key cannot contain the "%" character. Default key is the reserved prefix ".appconfig.featureflag/" + feature name.')
c.argument('requirement_type', arg_type=get_enum_type([FeatureFlagConstants.REQUIREMENT_TYPE_ALL, FeatureFlagConstants.REQUIREMENT_TYPE_ANY]),
help='Requirement type determines if filters should use "Any" or "All" logic when evaluating the state of a feature.')
c.argument('telemetry_enabled', arg_type=get_three_state_flag(), help='Enable or disable telemetry for the feature flag.')
c.argument('tags', arg_type=tags_type)

with self.argument_context('appconfig feature delete') as c:
Expand Down
15 changes: 14 additions & 1 deletion src/azure-cli/azure/cli/command_modules/appconfig/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
PublicNetworkAccess,
PrivateLinkDelegation,
DataPlaneProxyProperties,
AzureFrontDoorProperties)
AzureFrontDoorProperties,
TelemetryProperties)
from knack.log import get_logger
from ._utils import resolve_store_metadata, resolve_deleted_store_metadata
from ._constants import ARMAuthenticationMode, ProvisioningStatus
Expand Down Expand Up @@ -55,6 +56,7 @@ def create_configstore(cmd, # pylint: disable=too-many-locals
no_replica=None, # pylint: disable=unused-argument
arm_auth_mode=None,
enable_arm_private_network_access=None,
appinsights_resource=None,
kv_revision_retention_period=None,
azure_front_door_profile=None):
if assign_identity is not None and not assign_identity:
Expand All @@ -76,6 +78,10 @@ def create_configstore(cmd, # pylint: disable=too-many-locals
if azure_front_door_profile is not None:
azure_front_door = AzureFrontDoorProperties(resource_id=azure_front_door_profile if azure_front_door_profile else None)

telemetry = None
if appinsights_resource is not None:
telemetry = TelemetryProperties(resource_id=appinsights_resource if appinsights_resource else None)

configstore_params = ConfigurationStore(location=location.lower(),
identity=__get_resource_identity(assign_identity) if assign_identity else None,
sku=Sku(name=sku),
Expand All @@ -86,6 +92,7 @@ def create_configstore(cmd, # pylint: disable=too-many-locals
enable_purge_protection=enable_purge_protection,
create_mode=CreateMode.DEFAULT,
default_key_value_revision_retention_period_in_seconds=kv_revision_retention_period,
telemetry=telemetry,
data_plane_proxy=DataPlaneProxyProperties(
authentication_mode=arm_authentication_mode,
private_link_delegation=arm_private_link_delegation),
Expand Down Expand Up @@ -190,6 +197,7 @@ def update_configstore(cmd, # pylint: disable=too-many-locals
enable_purge_protection=None,
arm_auth_mode=None,
enable_arm_private_network_access=None,
appinsights_resource=None,
kv_revision_retention_period=None,
azure_front_door_profile=None):
__validate_cmk(encryption_key_name, encryption_key_vault, encryption_key_version, identity_client_id)
Expand All @@ -212,12 +220,17 @@ def update_configstore(cmd, # pylint: disable=too-many-locals
if azure_front_door_profile is not None:
azure_front_door = AzureFrontDoorProperties(resource_id=azure_front_door_profile if azure_front_door_profile else None)

telemetry = None
if appinsights_resource is not None:
telemetry = TelemetryProperties(resource_id=appinsights_resource if appinsights_resource else None)

update_params = ConfigurationStoreUpdateParameters(tags=tags,
sku=Sku(name=sku) if sku else None,
public_network_access=public_network_access,
disable_local_auth=disable_local_auth,
enable_purge_protection=enable_purge_protection,
default_key_value_revision_retention_period_in_seconds=kv_revision_retention_period,
telemetry=telemetry,
data_plane_proxy=DataPlaneProxyProperties(
authentication_mode=arm_authentication_mode,
private_link_delegation=arm_private_link_delegation),
Expand Down
47 changes: 45 additions & 2 deletions src/azure-cli/azure/cli/command_modules/appconfig/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,54 @@
convert_keyvalue_to_configurationsetting)
from ._utils import (get_appconfig_data_client,
prep_filter_for_url_encoding,
validate_feature_flag_name)
validate_feature_flag_name,
resolve_store_metadata)
from ._featuremodels import (map_keyvalue_to_featureflag,
map_keyvalue_to_featureflagvalue,
FeatureFilter)
FeatureFilter,
FeatureTelemetry)


logger = get_logger(__name__)

# Feature commands #


def warn_if_app_insights_not_set(cmd, store_name):
"""
Check if Application Insights resource is set for the App Configuration store.
Emits a warning if not set or if the check cannot be completed.
"""
from ._client_factory import cf_configstore

try:
resource_group_name, _ = resolve_store_metadata(cmd, store_name)
configstore_client = cf_configstore(cmd.cli_ctx)
store = configstore_client.get(resource_group_name, store_name)

telemetry = getattr(store, "telemetry", None)
is_linked = bool(getattr(telemetry, "resource_id", None)) if telemetry else False

if not is_linked:
logger.warning(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the logic behind this being a warning? This is actually something not supported by our portal UI.

Also, the warning is a bit wrong. If we do allow this, assuming the customer setup there application correctly, it will generate telemetry. It will just not be visible via the App Configuration Azure Portal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Portal, we show this warning and we disable enabling the flag's telemetry if app insights is not set.
image

The idea is just to show a warning if app insight is not linked to the store but allow users to enable telemetry for their flags. Just to confirm we support other avenues to collect telemetry other than app insights?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support other methods, they are just not built into the libraries.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we still allow enabling telemetry even though an app insights resource is not linked. would it be more accurate to say that telemetry will not be visible from the App Configuration portal if an app insights resource is not linked?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also remember from the email thread Christine had with us and some other folks that Zhenlan suggested we keep the portal warning but allow folks to enable/disable the telemetry toggle. We just won't show a graph if there is no linked app insights. I think this would unblock customers who might be using the other methods @mrm9084 is talking about in the event where they need to enable telemetry on portal/cli

"App Insights resource for the App Configuration store is not set."
"To collect telemetry, connect to an App Insights resource."
)

except Exception as ex: # pylint: disable=broad-except
logger.warning(
"Unable to verify App Insights resource for the App Configuration store: %s", str(ex)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also links to my comment on line 62. What is the actual failure case her in the verify? They have a linked store that was deleted, but still linked? Are we able to give a more helpful error message on our part.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception catches failures from resolve_metadata, which can fail if the logged-in user lacks sufficient permissions to list stores in the subscription or if the store name doesn't match any existing store. The specific error message from resolve_metadata should propagate and be caught by this exception.

)


def set_feature(cmd,
feature=None,
key=None,
name=None,
label=None,
description=None,
requirement_type=None,
telemetry_enabled=None,
yes=False,
connection_string=None,
auth_mode="key",
Expand Down Expand Up @@ -84,6 +114,12 @@ def set_feature(cmd,
FeatureFlagConstants.CONDITIONS: default_conditions
}

# Add telemetry if telemetry_enabled is specified
if telemetry_enabled is not None:
default_value[FeatureFlagConstants.TELEMETRY] = {FeatureFlagConstants.ENABLED: telemetry_enabled}
if telemetry_enabled:
warn_if_app_insights_not_set(cmd, name)

azconfig_client = get_appconfig_data_client(cmd, name, connection_string, auth_mode, endpoint)

retry_times = 3
Expand Down Expand Up @@ -135,6 +171,13 @@ def set_feature(cmd,
else FeatureFlagConstants.REQUIREMENT_TYPE_ANY
)

# Update telemetry if telemetry_enabled is specified
if telemetry_enabled is not None:
if feature_flag_value.telemetry is None:
feature_flag_value.telemetry = FeatureTelemetry(enabled=telemetry_enabled)
else:
feature_flag_value.telemetry.enabled = telemetry_enabled

set_kv = KeyValue(key=key,
label=label,
value=json.dumps(feature_flag_value, default=lambda o: {k: v for k, v in o.__dict__.items() if v is not None}, ensure_ascii=False),
Expand Down
Loading