def get_appconfig_data_client(cmd, name, connection_string, auth_mode, endpoint): azconfig_client = None if auth_mode == "key": connection_string = resolve_connection_string(cmd, name, connection_string) try: azconfig_client = AzureAppConfigurationClient.from_connection_string(connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) except ValueError as ex: raise CLIError("Failed to initialize AzureAppConfigurationClient due to an exception: {}".format(str(ex))) if auth_mode == "login": if not endpoint: try: if name: _, endpoint = resolve_store_metadata(cmd, name) else: raise CLIError("App Configuration endpoint or name should be provided if auth mode is 'login'.") except Exception as ex: raise CLIError(str(ex) + "\nYou may be able to resolve this issue by providing App Configuration endpoint instead of name.") from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cmd.cli_ctx) cred, _, _ = profile.get_login_credentials() try: azconfig_client = AzureAppConfigurationClient(credential=cred, base_url=endpoint, user_agent=HttpHeaders.USER_AGENT) except (ValueError, TypeError) as ex: raise CLIError("Failed to initialize AzureAppConfigurationClient due to an exception: {}".format(str(ex))) return azconfig_client
def test_mock_policies(self, client, appconfiguration_connection_string): from azure.core.pipeline.transport import HttpRequest, HttpResponse, HttpTransport from azure.core.pipeline.policies import RetryPolicy from azure.core.pipeline import Pipeline, PipelineResponse class MockTransport(HttpTransport): def __init__(self): self._count = 0 self.auth_headers = None def __exit__(self, exc_type, exc_val, exc_tb): pass def close(self): pass def open(self): pass def send(self, request, **kwargs): # type: (PipelineRequest, Any) -> PipelineResponse assert request.headers['Authorization'] != self.auth_headers self.auth_headers = request.headers['Authorization'] response = HttpResponse(request, None) response.status_code = 429 return response def new_method(self, request): request.http_request.headers["Authorization"] = uuid4() from azure.appconfiguration._azure_appconfiguration_requests import AppConfigRequestsCredentialsPolicy temp = AppConfigRequestsCredentialsPolicy._signed_request AppConfigRequestsCredentialsPolicy._signed_request = new_method client = AzureAppConfigurationClient.from_connection_string(appconfiguration_connection_string, transport=MockTransport()) client.list_configuration_settings() AppConfigRequestsCredentialsPolicy._signed_request = temp
def main(): CONNECTION_STRING = get_connection_string() # Create app config client client = AzureAppConfigurationClient.from_connection_string(CONNECTION_STRING) config_setting = ConfigurationSetting( key="MyKey", value="my value", content_type="my content type", tags={"my tag": "my tag value"} ) returned_config_setting = client.set_configuration_setting(config_setting) returned_config_setting.value = "new value" returned_config_setting.content_type = "new content type" client.set_configuration_setting(config_setting) items = client.list_revisions(key_filter="MyKey") for item in items: print_configuration_setting(item) print("") client.delete_configuration_setting( key="MyKey", )
def main(): CONNECTION_STRING = get_connection_string() # Create app config client client = AzureAppConfigurationClient.from_connection_string( CONNECTION_STRING) print("Add new configuration 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) print("New configuration setting:") print_configuration_setting(added_config_setting) print("") print("Set configuration setting") added_config_setting.value = "new value" added_config_setting.content_type = "new content type" updated_config_setting = client.set_configuration_setting(config_setting) print_configuration_setting(updated_config_setting) print("") print("List configuration settings") config_settings = client.list_configuration_settings(labels=["MyLabel"]) for item in config_settings: print_configuration_setting(item) print("Delete configuration setting") client.delete_configuration_setting( key="MyKey", label="MyLabel", )
def main(): CONNECTION_STRING = get_connection_string() # Create app config client client = AzureAppConfigurationClient.from_connection_string(CONNECTION_STRING) print("Set new configuration setting") config_setting = ConfigurationSetting( key="MyKey", value="my value", content_type="my content type", tags={"my tag": "my tag value"} ) returned_config_setting = client.set_configuration_setting(config_setting) print("New configuration setting:") print_configuration_setting(returned_config_setting) print("") print("Get configuration setting") fetched_config_setting = client.get_configuration_setting( key="MyKey" ) print("Fetched configuration setting:") print_configuration_setting(fetched_config_setting) print("") print("Delete configuration setting") client.delete_configuration_setting( key="MyKey" )
def list_key(cmd, key=None, fields=None, name=None, label=None, datetime=None, connection_string=None, top=None, all_=False, resolve_keyvault=False): if fields and resolve_keyvault: raise CLIError( "Please provide only one of these arguments: '--fields' or '--resolve-keyvault'. See 'az appconfig kv list -h' for examples." ) connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) keyvalues = __read_kv_from_config_store( azconfig_client, key=key if key else SearchFilterOptions.ANY_KEY, label=label if label else SearchFilterOptions.ANY_LABEL, datetime=datetime, fields=fields, top=top, all_=all_, cli_ctx=cmd.cli_ctx if resolve_keyvault else None) return keyvalues
def __init__(self): self.client = AzureAppConfigurationClient.from_connection_string(APP_CONFIG_CONNECTION_STR) self.api_instance = API_INSTANCE_NAME # sentinel should change if new configurations are available self.sentinel = self._get_sentinel() self.allowlist = self._get_allowlist()
def __init__(self, method_name): super(AppConfigurationClientTest, self).__init__(method_name) self.vcr.match_on = ["path", "method", "query"] if self.is_playback(): connection_str = "Endpoint=https://fake_app_config.azconfig-test.io;Id=0-l4-s0:h5htBaY5Z1LwFz50bIQv;Secret=bgyvBgwsQIw0s8myrqJJI3nLrj81M/kzSgSuP4BBoVg=" else: connection_str = os.getenv('APP_CONFIG_CONNECTION') self.app_config_client = AzureAppConfigurationClient.from_connection_string(connection_str)
def __init__(self, arguments): super().__init__(arguments) connection_string = self.get_from_env( "AZURE_APP_CONFIG_CONNECTION_STRING") self.key = "KEY" self.service_client = SyncAppConfigClient.from_connection_string( connection_string=connection_string) self.async_service_client = AsyncAppConfigClient.from_connection_string( connection_string=connection_string)
def unlock_feature(cmd, feature, name=None, label=None, connection_string=None, yes=False): key = FeatureFlagConstants.FEATURE_FLAG_PREFIX + feature connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) retry_times = 3 retry_interval = 1 for i in range(0, retry_times): try: retrieved_kv = azconfig_client.get_configuration_setting( key=key, label=label) except ResourceNotFoundError: raise CLIError( "Feature '{}' with label '{}' does not exist.".format( feature, label)) except HttpResponseError as exception: raise CLIError( "Failed to retrieve feature flags from config store. " + str(exception)) if retrieved_kv is None or retrieved_kv.content_type != FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: raise CLIError( "The feature '{}' you are trying to unlock does not exist.". format(feature)) confirmation_message = "Are you sure you want to unlock the feature '{}' with label '{}'".format( feature, label) user_confirmation(confirmation_message, yes) try: new_kv = azconfig_client.set_read_only( retrieved_kv, read_only=False, match_condition=MatchConditions.IfNotModified) return map_keyvalue_to_featureflag( convert_configurationsetting_to_keyvalue(new_kv), show_conditions=False) except HttpResponseError as exception: if exception.status_code == StatusCodes.PRECONDITION_FAILED: logger.debug( 'Retrying unlock operation %s times with exception: concurrent setting operations', i + 1) time.sleep(retry_interval) else: raise CLIError(str(exception)) except Exception as exception: raise CLIError(str(exception)) raise CLIError( "Failed to unlock the feature '{}' with label '{}' due to a conflicting operation." .format(feature, label))
def azure_appconfig_resolver(connection_string): client = AzureAppConfigurationClient.from_connection_string( connection_string) def return_config(k): try: return client.get_configuration_setting(k).value except ResourceNotFoundError as rnfe: raise KeyError from rnfe return return_config
def wrapper(*args, **kwargs): appconfiguration_connection_string = kwargs.pop( "appconfiguration_connection_string") client = AzureAppConfigurationClient.from_connection_string( appconfiguration_connection_string) kwargs['client'] = client kwargs[ 'appconfiguration_connection_string'] = appconfiguration_connection_string # Do setUp on client test_config_setting = _add_for_test( client, ConfigurationSetting( key=KEY, label=LABEL, value=TEST_VALUE, content_type=TEST_CONTENT_TYPE, tags={ "tag1": "tag1", "tag2": "tag2" }, )) test_config_setting_no_label = _add_for_test( client, ConfigurationSetting( key=KEY, label=None, value=TEST_VALUE, content_type=TEST_CONTENT_TYPE, tags={ "tag1": "tag1", "tag2": "tag2" }, )) to_delete = [test_config_setting, test_config_setting_no_label] kwargs['test_config_setting'] = test_config_setting kwargs['test_config_setting_no_label'] = test_config_setting_no_label trimmed_kwargs = {k: v for k, v in kwargs.items()} trim_kwargs_from_test_function(func, trimmed_kwargs) func(*args, **trimmed_kwargs) for item in to_delete: try: client.delete_configuration_setting(key=item.key, label=item.label) except: print("Issue deleting config with key {} and label {}".format( item.key, item.label))
def create_kv(self, connection_str): from azure.appconfiguration import (AzureAppConfigurationClient, ConfigurationSetting) app_config_client = AzureAppConfigurationClient.from_connection_string( connection_str) kv = ConfigurationSetting(key=KEY, label=LABEL, value=TEST_VALUE, content_type=TEST_CONTENT_TYPE, tags={ "tag1": "tag1", "tag2": "tag2" }) created_kv = app_config_client.add_configuration_setting(kv) return created_kv
def list_revision(cmd, key=None, fields=None, name=None, label=None, datetime=None, connection_string=None, top=None, all_=False): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) key = key if key else SearchFilterOptions.ANY_KEY label = label if label else SearchFilterOptions.ANY_LABEL if label == SearchFilterOptions.EMPTY_LABEL: label = prep_null_label_for_url_encoding(label) try: revisions_iterable = azconfig_client.list_revisions( key_filter=key, label_filter=label, accept_datetime=datetime, fields=fields) retrieved_revisions = [] count = 0 if all_: top = float('inf') elif top is None: top = 100 for revision in revisions_iterable: kv_revision = convert_configurationsetting_to_keyvalue(revision) if fields: partial_revision = {} for field in fields: partial_revision[field.name.lower( )] = kv_revision.__dict__[field.name.lower()] retrieved_revisions.append(partial_revision) else: retrieved_revisions.append(kv_revision) count += 1 if count >= top: return retrieved_revisions return retrieved_revisions except HttpResponseError as ex: raise CLIError('List revision operation failed.\n' + str(ex))
def handle_event_grid_notifications(event_grid_events): # type: (List[dict[str, Any]]) -> None CONNECTION_STRING = get_connection_string() all_keys = [] with AzureAppConfigurationClient.from_connection_string(CONNECTION_STRING) as client: for event_grid_event in event_grid_events: if event_grid_event["eventType"] == 'Microsoft.KeyValueModified': sync_token = event['data']['syncToken'] client.update_sync_token(sync_token) new_key = client.get_configuration_setting(key=event['data']['key'], label=event['data']['label']) all_keys.append(new_key)
def list_feature(cmd, feature=None, name=None, label=None, fields=None, connection_string=None, top=None, all_=False): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) try: retrieved_keyvalues = __list_all_keyvalues( azconfig_client, feature=feature if feature else SearchFilterOptions.ANY_KEY, label=label if label else SearchFilterOptions.ANY_LABEL) retrieved_featureflags = [] for kv in retrieved_keyvalues: retrieved_featureflags.append( map_keyvalue_to_featureflag(keyvalue=kv, show_conditions=True)) filtered_featureflags = [] count = 0 if all_: top = len(retrieved_featureflags) elif top is None: top = 100 for featureflag in retrieved_featureflags: if fields: partial_featureflags = {} for field in fields: # featureflag is guaranteed to have all the fields because # we validate this in map_keyvalue_to_featureflag() # So this line will never throw AttributeError partial_featureflags[field.name.lower()] = getattr( featureflag, field.name.lower()) filtered_featureflags.append(partial_featureflags) else: filtered_featureflags.append(featureflag) count += 1 if count >= top: break return filtered_featureflags except Exception as exception: raise CLIError(str(exception))
def unlock_key(cmd, key, label=None, name=None, connection_string=None, yes=False): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) retry_times = 3 retry_interval = 1 for i in range(0, retry_times): try: retrieved_kv = azconfig_client.get_configuration_setting( key=key, label=label) except ResourceNotFoundError: raise CLIError("Key '{}' with label '{}' does not exist.".format( key, label)) except HttpResponseError as exception: raise CLIError(str(exception)) confirmation_message = "Are you sure you want to unlock the key '{}' with label '{}'".format( key, label) user_confirmation(confirmation_message, yes) try: new_kv = azconfig_client.set_read_only( retrieved_kv, read_only=False, match_condition=MatchConditions.IfNotModified) return convert_configurationsetting_to_keyvalue(new_kv) except HttpResponseError as exception: if exception.status_code == StatusCodes.PRECONDITION_FAILED: logger.debug( 'Retrying unlock operation %s times with exception: concurrent setting operations', i + 1) time.sleep(retry_interval) else: raise CLIError(str(exception)) except Exception as exception: raise CLIError(str(exception)) raise CLIError( "Failed to unlock the key '{}' with label '{}' due to a conflicting operation." .format(key, label))
def list_filter(cmd, feature, name=None, label=None, connection_string=None, top=None, all_=False): key = FeatureFlagConstants.FEATURE_FLAG_PREFIX + feature connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) try: retrieved_kv = azconfig_client.get_configuration_setting(key=key, label=label) except ResourceNotFoundError: raise CLIError("Feature flag '{}' with label '{}' not found.".format( feature, label)) except HttpResponseError as exception: raise CLIError("Failed to retrieve feature flags from config store. " + str(exception)) try: if retrieved_kv is None or retrieved_kv.content_type != FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: raise CLIError( "The feature flag {} does not exist.".format(feature)) # we make sure that value retrieved is a valid json and only has the fields supported by backend. # if it's invalid, we catch appropriate exception that contains # detailed message feature_flag_value = map_keyvalue_to_featureflagvalue(retrieved_kv) feature_filters = feature_flag_value.conditions['client_filters'] if all_: top = len(feature_filters) elif top is None: top = 100 return feature_filters[:top] except Exception as exception: raise CLIError(str(exception))
def show_feature(cmd, feature, name=None, label=None, fields=None, connection_string=None): key = FeatureFlagConstants.FEATURE_FLAG_PREFIX + feature connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) try: config_setting = azconfig_client.get_configuration_setting(key=key, label=label) if config_setting is None or config_setting.content_type != FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: raise CLIError("The feature flag does not exist.") retrieved_kv = convert_configurationsetting_to_keyvalue(config_setting) feature_flag = map_keyvalue_to_featureflag(keyvalue=retrieved_kv, show_conditions=True) # If user has specified fields, we still get all the fields and then # filter what we need from the response. if fields: partial_featureflag = {} for field in fields: # feature_flag is guaranteed to have all the fields because # we validate this in map_keyvalue_to_featureflag() # So this line will never throw AttributeError partial_featureflag[field.name.lower()] = getattr( feature_flag, field.name.lower()) return partial_featureflag return feature_flag except ResourceNotFoundError: raise CLIError("Feature '{}' with label '{}' does not exist.".format( feature, label)) except HttpResponseError as exception: raise CLIError(str(exception)) raise CLIError("Failed to get the feature '{}' with label '{}'.".format( feature, label))
def main(): CONNECTION_STRING = get_connection_string() # Create app config client client = AzureAppConfigurationClient.from_connection_string( CONNECTION_STRING) # Unconditional set config_setting = ConfigurationSetting(key="MyKey", value="my value", content_type="my content type", tags={"my tag": "my tag value"}) client.set_configuration_setting(config_setting) # Unconditional get first_get = client.get_configuration_setting(key="MyKey") print_configuration_setting(first_get) # Conditional get, expect to return None because it is not modified second_get = client.get_configuration_setting( key="MyKey", etag=first_get.etag, match_condition=MatchConditions.IfModified) print_configuration_setting(second_get) # Conditional set first_get.value = "new value" client.set_configuration_setting( configuration_setting=first_get, match_condition=MatchConditions.IfNotModified) # Conditional set, expect to see error because it is modified try: client.set_configuration_setting( configuration_setting=first_get, match_condition=MatchConditions.IfNotModified) except ResourceModifiedError: pass client.delete_configuration_setting(key="MyKey")
def main(): CONNECTION_STRING = get_connection_string() # Create app config client client = AzureAppConfigurationClient.from_connection_string(CONNECTION_STRING) print("Set new configuration setting") config_setting = ConfigurationSetting( key="MyKey", value="my value", content_type="my content type", tags={"my tag": "my tag value"} ) returned_config_setting = client.set_configuration_setting(config_setting) print("New configuration setting:") print_configuration_setting(returned_config_setting) print("") print("Read only configuration setting:") read_only_config_setting = client.set_read_only( returned_config_setting ) print_configuration_setting(read_only_config_setting) print("") print("Clear read only configuration setting:") read_write_config_setting = client.set_read_only( returned_config_setting, False ) print_configuration_setting(read_write_config_setting) print("") print("Delete configuration setting") client.delete_configuration_setting( key="MyKey", )
def show_key(cmd, key, name=None, label=None, datetime=None, connection_string=None): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) try: key_value = azconfig_client.get_configuration_setting( key=key, label=label, accept_datetime=datetime) if key_value is None: raise CLIError("The key-value does not exist.") return convert_configurationsetting_to_keyvalue(key_value) except ResourceNotFoundError: raise CLIError("Key '{}' with label '{}' does not exist.".format( key, label)) except HttpResponseError as exception: raise CLIError(str(exception)) raise CLIError("Failed to get the key '{}' with label '{}'.".format( key, label))
def get_azure_app_configuration_client(self): connection_str = self.config.AZURE_APP_CONFIGURATION_CONNECTION_STRING client = AzureAppConfigurationClient.from_connection_string( connection_str) return client
def export_config( cmd, destination, name=None, connection_string=None, label=None, key=None, prefix="", # prefix to remove yes=False, skip_features=False, skip_keyvault=False, # to-file parameters path=None, format_=None, separator=None, naming_convention='pascal', resolve_keyvault=False, # to-config-store parameters dest_name=None, dest_connection_string=None, dest_label=None, preserve_labels=False, # to-app-service parameters appservice_account=None): src_features = [] dest_features = [] dest_kvs = [] destination = destination.lower() format_ = format_.lower() if format_ else None naming_convention = naming_convention.lower() connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) dest_azconfig_client = None if destination == 'appconfig': if dest_label is not None and preserve_labels: raise CLIError( "Export failed! Please provide only one of these arguments: '--dest-label' or '--preserve-labels'. See 'az appconfig kv export -h' for examples." ) if preserve_labels: # We need dest_label to be the same as label for preview later. # This will have no effect on label while writing to config store # as we check preserve_labels again before labelling KVs. dest_label = label dest_connection_string = resolve_connection_string( cmd, dest_name, dest_connection_string) dest_azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=dest_connection_string, user_agent=HttpHeaders.USER_AGENT) # fetch key values from user's configstore src_kvs = __read_kv_from_config_store( azconfig_client, key=key, label=label if label else SearchFilterOptions.EMPTY_LABEL, prefix_to_remove=prefix, cli_ctx=cmd.cli_ctx if resolve_keyvault else None) if skip_keyvault: src_kvs = [ keyvalue for keyvalue in src_kvs if keyvalue.content_type != KeyVaultConstants.KEYVAULT_CONTENT_TYPE ] # We need to separate KV from feature flags __discard_features_from_retrieved_kv(src_kvs) if not skip_features: # Get all Feature flags with matching label if (destination == 'file' and format_ == 'properties') or destination == 'appservice': skip_features = True logger.warning( "Exporting feature flags to properties file or appservice is currently not supported." ) else: # src_features is a list of FeatureFlag objects src_features = list_feature( cmd, feature='*', label=label if label else SearchFilterOptions.EMPTY_LABEL, name=name, connection_string=connection_string, all_=True) # if customer needs preview & confirmation if not yes: if destination == 'appconfig': # dest_kvs contains features and KV that match the label dest_kvs = __read_kv_from_config_store( dest_azconfig_client, key=SearchFilterOptions.ANY_KEY, label=dest_label if dest_label else SearchFilterOptions.EMPTY_LABEL) __discard_features_from_retrieved_kv(dest_kvs) if not skip_features: # Append all features to dest_features list dest_features = list_feature( cmd, feature='*', label=dest_label if dest_label else SearchFilterOptions.EMPTY_LABEL, name=dest_name, connection_string=dest_connection_string, all_=True) elif destination == 'appservice': dest_kvs = __read_kv_from_app_service( cmd, appservice_account=appservice_account) # generate preview and wait for user confirmation need_kv_change = __print_preview( old_json=__serialize_kv_list_to_comparable_json_object( keyvalues=dest_kvs, level=destination), new_json=__serialize_kv_list_to_comparable_json_object( keyvalues=src_kvs, level=destination)) need_feature_change = False if src_features: need_feature_change = __print_features_preview( old_json=__serialize_feature_list_to_comparable_json_object( features=dest_features), new_json=__serialize_feature_list_to_comparable_json_object( features=src_features)) if not need_kv_change and not need_feature_change: return user_confirmation("Do you want to continue? \n") # export to destination if destination == 'file': __write_kv_and_features_to_file(file_path=path, key_values=src_kvs, features=src_features, format_=format_, separator=separator, skip_features=skip_features, naming_convention=naming_convention) elif destination == 'appconfig': __write_kv_and_features_to_config_store( dest_azconfig_client, key_values=src_kvs, features=src_features, label=dest_label, preserve_labels=preserve_labels) elif destination == 'appservice': __write_kv_to_app_service(cmd, key_values=src_kvs, appservice_account=appservice_account)
def set_key(cmd, key, name=None, label=None, content_type=None, tags=None, value=None, yes=False, connection_string=None): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) if content_type: if content_type.lower() == KeyVaultConstants.KEYVAULT_CONTENT_TYPE: logger.warning( "There is a dedicated command to set key vault reference. 'appconfig kv set-keyvault -h'" ) elif content_type.lower( ) == FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: logger.warning( "There is a dedicated command to set feature flag. 'appconfig feature set -h'" ) retry_times = 3 retry_interval = 1 label = label if label and label != SearchFilterOptions.EMPTY_LABEL else None for i in range(0, retry_times): retrieved_kv = None set_kv = None new_kv = None try: retrieved_kv = azconfig_client.get_configuration_setting( key=key, label=label) except ResourceNotFoundError: logger.debug( "Key '%s' with label '%s' not found. A new key-value will be created.", key, label) except HttpResponseError as exception: raise CLIError( "Failed to retrieve key-values from config store. " + str(exception)) if retrieved_kv is None: if content_type and __is_json_content_type(content_type): try: # Ensure that provided value is valid JSON. Error out if value is invalid JSON. value = 'null' if value is None else value json.loads(value) except ValueError: raise CLIError( 'Value "{}" is not a valid JSON object, which conflicts with the content type "{}".' .format(value, content_type)) set_kv = ConfigurationSetting(key=key, label=label, value=value, content_type=content_type, tags=tags) else: value = retrieved_kv.value if value is None else value content_type = retrieved_kv.content_type if content_type is None else content_type if content_type and __is_json_content_type(content_type): try: # Ensure that provided/existing value is valid JSON. Error out if value is invalid JSON. json.loads(value) except (TypeError, ValueError): raise CLIError( 'Value "{}" is not a valid JSON object, which conflicts with the content type "{}". Set the value again in valid JSON format.' .format(value, content_type)) set_kv = ConfigurationSetting( key=key, label=label, value=value, content_type=content_type, tags=retrieved_kv.tags if tags is None else tags, read_only=retrieved_kv.read_only, etag=retrieved_kv.etag) verification_kv = { "key": set_kv.key, "label": set_kv.label, "content_type": set_kv.content_type, "value": set_kv.value, "tags": set_kv.tags } entry = json.dumps(verification_kv, indent=2, sort_keys=True, ensure_ascii=False) confirmation_message = "Are you sure you want to set the key: \n" + entry + "\n" user_confirmation(confirmation_message, yes) try: if set_kv.etag is None: new_kv = azconfig_client.add_configuration_setting(set_kv) else: new_kv = azconfig_client.set_configuration_setting( set_kv, match_condition=MatchConditions.IfNotModified) return convert_configurationsetting_to_keyvalue(new_kv) except ResourceReadOnlyError: raise CLIError( "Failed to update read only key-value. Unlock the key-value before updating it." ) except HttpResponseError as exception: if exception.status_code == StatusCodes.PRECONDITION_FAILED: logger.debug( 'Retrying setting %s times with exception: concurrent setting operations', i + 1) time.sleep(retry_interval) else: raise CLIError(str(exception)) except Exception as exception: raise CLIError(str(exception)) raise CLIError( "Failed to set the key '{}' due to a conflicting operation.".format( key))
from azure.appconfiguration import AzureAppConfigurationClient connection_str = "Endpoint=https://config-demo-azure.azconfig.io;Id=/sWh-l9-s0:n06YX28m2mvHmdknfJdx;Secret=lVepuj2LbqdRjl5dHDXc1U/M7Q0QkXlt71Qhdq6SxSE=" client = AzureAppConfigurationClient.from_connection_string(connection_str) def get_tweeter_config(): tweeter_conf = {} tweeter_conf['access_token'] = client.get_configuration_setting( key="tweeter_access_token").value tweeter_conf['access_token_secret'] = client.get_configuration_setting( key="tweeter_access_token_secret").value tweeter_conf['consumer_key'] = client.get_configuration_setting( key="tweeter_consumer_key").value tweeter_conf['consumer_secret'] = client.get_configuration_setting( key="tweeter_consumer_secret").value tweeter_conf['tweets_number'] = client.get_configuration_setting( key="tweets_number").value return tweeter_conf def get_eventhub_config(): eventhub_conf = {} eventhub_conf['tweeter-name'] = client.get_configuration_setting( key="eventhub-tweeter-name").value eventhub_conf['connection_string'] = client.get_configuration_setting( key="evet_hub_connection_string").value return eventhub_conf
def import_config( cmd, source, name=None, connection_string=None, label=None, prefix="", # prefix to add yes=False, skip_features=False, content_type=None, # from-file parameters path=None, format_=None, separator=None, depth=None, # from-configstore parameters src_name=None, src_connection_string=None, src_key=None, src_label=None, preserve_labels=False, # from-appservice parameters appservice_account=None): src_features = [] dest_features = [] dest_kvs = [] source = source.lower() format_ = format_.lower() if format_ else None connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) # fetch key values from source if source == 'file': if format_ and content_type: # JSON content type is only supported with JSON format. # Error out if user has provided JSON content type with any other format. if format_ != 'json' and __is_json_content_type(content_type): raise CLIError( "Failed to import '{}' file format with '{}' content type. Please provide JSON file format to match your content type." .format(format_, content_type)) if separator: # If separator is provided, use max depth by default unless depth is specified. depth = sys.maxsize if depth is None else int(depth) else: if depth and int(depth) != 1: logger.warning( "Cannot flatten hierarchical data without a separator. --depth argument will be ignored." ) depth = 1 src_kvs = __read_kv_from_file(file_path=path, format_=format_, separator=separator, prefix_to_add=prefix, depth=depth, content_type=content_type) if not skip_features: # src_features is a list of KeyValue objects src_features = __read_features_from_file(file_path=path, format_=format_) elif source == 'appconfig': src_connection_string = resolve_connection_string( cmd, src_name, src_connection_string) src_azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=src_connection_string, user_agent=HttpHeaders.USER_AGENT) if label is not None and preserve_labels: raise CLIError( "Import failed! Please provide only one of these arguments: '--label' or '--preserve-labels'. See 'az appconfig kv import -h' for examples." ) if preserve_labels: # We need label to be the same as src_label for preview later. # This will have no effect on label while writing to config store # as we check preserve_labels again before labelling KVs. label = src_label src_kvs = __read_kv_from_config_store( src_azconfig_client, key=src_key, label=src_label if src_label else SearchFilterOptions.EMPTY_LABEL, prefix_to_add=prefix) # We need to separate KV from feature flags __discard_features_from_retrieved_kv(src_kvs) if not skip_features: # Get all Feature flags with matching label all_features = __read_kv_from_config_store( src_azconfig_client, key=FeatureFlagConstants.FEATURE_FLAG_PREFIX + '*', label=src_label if src_label else SearchFilterOptions.EMPTY_LABEL) for feature in all_features: if feature.content_type == FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: src_features.append(feature) elif source == 'appservice': src_kvs = __read_kv_from_app_service( cmd, appservice_account=appservice_account, prefix_to_add=prefix, content_type=content_type) # if customer needs preview & confirmation if not yes: # fetch key values from user's configstore dest_kvs = __read_kv_from_config_store( azconfig_client, key=SearchFilterOptions.ANY_KEY, label=label if label else SearchFilterOptions.EMPTY_LABEL) __discard_features_from_retrieved_kv(dest_kvs) # generate preview and wait for user confirmation need_kv_change = __print_preview( old_json=__serialize_kv_list_to_comparable_json_object( keyvalues=dest_kvs, level=source), new_json=__serialize_kv_list_to_comparable_json_object( keyvalues=src_kvs, level=source)) need_feature_change = False if src_features and not skip_features: # Append all features to dest_features list all_features = __read_kv_from_config_store( azconfig_client, key=FeatureFlagConstants.FEATURE_FLAG_PREFIX + '*', label=label if label else SearchFilterOptions.EMPTY_LABEL) for feature in all_features: if feature.content_type == FeatureFlagConstants.FEATURE_FLAG_CONTENT_TYPE: dest_features.append(feature) need_feature_change = __print_features_preview( old_json= __serialize_features_from_kv_list_to_comparable_json_object( keyvalues=dest_features), new_json= __serialize_features_from_kv_list_to_comparable_json_object( keyvalues=src_features)) if not need_kv_change and not need_feature_change: return user_confirmation("Do you want to continue? \n") # append all feature flags to src_kvs list src_kvs.extend(src_features) # import into configstore __write_kv_and_features_to_config_store(azconfig_client, key_values=src_kvs, label=label, preserve_labels=preserve_labels, content_type=content_type)
def set_keyvault(cmd, key, secret_identifier, name=None, label=None, tags=None, yes=False, connection_string=None): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) keyvault_ref_value = json.dumps({"uri": secret_identifier}, ensure_ascii=False, separators=(',', ':')) retry_times = 3 retry_interval = 1 label = label if label and label != SearchFilterOptions.EMPTY_LABEL else None for i in range(0, retry_times): retrieved_kv = None set_kv = None new_kv = None try: retrieved_kv = azconfig_client.get_configuration_setting( key=key, label=label) except ResourceNotFoundError: logger.debug( "Key '%s' with label '%s' not found. A new key-vault reference will be created.", key, label) except HttpResponseError as exception: raise CLIError( "Failed to retrieve key-values from config store. " + str(exception)) if retrieved_kv is None: set_kv = ConfigurationSetting( key=key, label=label, value=keyvault_ref_value, content_type=KeyVaultConstants.KEYVAULT_CONTENT_TYPE, tags=tags) else: set_kv = ConfigurationSetting( key=key, label=label, value=keyvault_ref_value, content_type=KeyVaultConstants.KEYVAULT_CONTENT_TYPE, tags=retrieved_kv.tags if tags is None else tags, read_only=retrieved_kv.read_only, etag=retrieved_kv.etag) verification_kv = { "key": set_kv.key, "label": set_kv.label, "content_type": set_kv.content_type, "value": set_kv.value, "tags": set_kv.tags } entry = json.dumps(verification_kv, indent=2, sort_keys=True, ensure_ascii=False) confirmation_message = "Are you sure you want to set the keyvault reference: \n" + entry + "\n" user_confirmation(confirmation_message, yes) try: if set_kv.etag is None: new_kv = azconfig_client.add_configuration_setting(set_kv) else: new_kv = azconfig_client.set_configuration_setting( set_kv, match_condition=MatchConditions.IfNotModified) return convert_configurationsetting_to_keyvalue(new_kv) except ResourceReadOnlyError: raise CLIError( "Failed to update read only key vault reference. Unlock the key vault reference before updating it." ) except HttpResponseError as exception: if exception.status_code == StatusCodes.PRECONDITION_FAILED: logger.debug( 'Retrying setting %s times with exception: concurrent setting operations', i + 1) time.sleep(retry_interval) else: raise CLIError(str(exception)) except Exception as exception: raise CLIError(str(exception)) raise CLIError( "Failed to set the keyvault reference '{}' due to a conflicting operation." .format(key))
def delete_key(cmd, key, name=None, label=None, yes=False, connection_string=None): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) # list_configuration_settings returns kv with null label when: # label = ASCII null 0x00, or URL encoded %00 # In delete, import and export commands, we treat missing --label as null label # In list, restore and revision commands, we treat missing --label as all labels entries = __read_kv_from_config_store( azconfig_client, key=key, label=label if label else SearchFilterOptions.EMPTY_LABEL) confirmation_message = "Found '{}' key-values matching the specified key and label. Are you sure you want to delete these key-values?".format( len(entries)) user_confirmation(confirmation_message, yes) deleted_entries = [] exception_messages = [] for entry in entries: try: deleted_kv = azconfig_client.delete_configuration_setting( key=entry.key, label=entry.label, etag=entry.etag, match_condition=MatchConditions.IfNotModified) deleted_entries.append( convert_configurationsetting_to_keyvalue(deleted_kv)) except ResourceReadOnlyError: exception = "Failed to delete read-only key-value with key '{}' and label '{}'. Unlock the key-value before deleting it.".format( entry.key, entry.label) exception_messages.append(exception) except ResourceModifiedError: exception = "Failed to delete key-value with key '{}' and label '{}' due to a conflicting operation.".format( entry.key, entry.label) exception_messages.append(exception) except HttpResponseError as ex: exception_messages.append(str(ex)) raise CLIError( 'Delete operation failed. The following error(s) occurred:\n' + json.dumps(exception_messages, indent=2, ensure_ascii=False)) # Log errors if partially succeeded if exception_messages: if deleted_entries: logger.error( 'Delete operation partially failed. The following error(s) occurred:\n%s\n', json.dumps(exception_messages, indent=2, ensure_ascii=False)) else: raise CLIError( 'Delete operation failed. \n' + json.dumps(exception_messages, indent=2, ensure_ascii=False)) return deleted_entries
def restore_key(cmd, datetime, key=None, name=None, label=None, connection_string=None, yes=False): connection_string = resolve_connection_string(cmd, name, connection_string) azconfig_client = AzureAppConfigurationClient.from_connection_string( connection_string=connection_string, user_agent=HttpHeaders.USER_AGENT) exception_messages = [] try: restore_keyvalues = __read_kv_from_config_store( azconfig_client, key=key if key else SearchFilterOptions.ANY_KEY, label=label if label else SearchFilterOptions.ANY_LABEL, datetime=datetime) current_keyvalues = __read_kv_from_config_store( azconfig_client, key=key if key else SearchFilterOptions.ANY_KEY, label=label if label else SearchFilterOptions.ANY_LABEL) except HttpResponseError as exception: raise CLIError( 'Failed to read key-value(s) that match the specified key and label.' + str(exception)) try: kvs_to_restore, kvs_to_modify, kvs_to_delete = __compare_kvs_for_restore( restore_keyvalues, current_keyvalues) if not yes: need_change = __print_restore_preview(kvs_to_restore, kvs_to_modify, kvs_to_delete) if need_change is False: logger.debug( 'Canceling the restore operation based on user selection.') return keys_to_restore = len(kvs_to_restore) + len(kvs_to_modify) + len( kvs_to_delete) restored_so_far = 0 for kv in chain(kvs_to_restore, kvs_to_modify): set_kv = convert_keyvalue_to_configurationsetting(kv) try: azconfig_client.set_configuration_setting(set_kv) restored_so_far += 1 except ResourceReadOnlyError: exception = "Failed to update read-only key-value with key '{}' and label '{}'. Unlock the key-value before updating it.".format( set_kv.key, set_kv.label) exception_messages.append(exception) except ResourceModifiedError: exception = "Failed to update key-value with key '{}' and label '{}' due to a conflicting operation.".format( set_kv.key, set_kv.label) exception_messages.append(exception) for kv in kvs_to_delete: try: azconfig_client.delete_configuration_setting( key=kv.key, label=kv.label, etag=kv.etag, match_condition=MatchConditions.IfNotModified) restored_so_far += 1 except ResourceReadOnlyError: exception = "Failed to delete read-only key-value with key '{}' and label '{}'. Unlock the key-value before deleting it.".format( kv.key, kv.label) exception_messages.append(exception) except ResourceModifiedError: exception = "Failed to delete key-value with key '{}' and label '{}' due to a conflicting operation.".format( kv.key, kv.label) exception_messages.append(exception) if restored_so_far != keys_to_restore: logger.error( 'Failed after restoring %d out of %d keys. The following error(s) occurred:\n%s\n', restored_so_far, keys_to_restore, json.dumps(exception_messages, indent=2, ensure_ascii=False)) else: logger.debug('Successfully restored %d out of %d keys', restored_so_far, keys_to_restore) return except HttpResponseError as ex: exception_messages.append(str(ex)) raise CLIError( 'Restore operation failed. The following error(s) occurred:\n' + json.dumps(exception_messages, indent=2, ensure_ascii=False))