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
1 change: 1 addition & 0 deletions src/azure-cli/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Release History
**AppConfig**

* Add support for importing/exporting feature flags
* Add new command 'az appconfig kv set-keyvault' for creating keyvault reference

**AppService**

Expand Down
10 changes: 10 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 @@ -138,6 +138,16 @@
text: az appconfig kv set --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --key color --value red --tags key1=value1 key2=value2
"""

helps['appconfig kv set-keyvault'] = """
type: command
short-summary: Set a keyvault reference.
examples:
- name: Set a keyvault reference with label MyLabel.
text: az appconfig kv set-keyvault -n MyAppConfiguration --key HostSecret --label MyLabel --secret-identifier https://contoso.vault.azure.net/Secrets/DummySecret/Dummyversion
- name: Set a keyvault reference with null label and multiple tags using connection string.
text: az appconfig kv set-keyvault --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --key HostSecret --secret-identifier https://contoso.vault.azure.net/Secrets/DummySecret --tags tag1=value1 tag2=value2
"""

helps['appconfig kv show'] = """
type: command
short-summary: Show all attributes of a key-value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
validate_export, validate_import,
validate_import_depth, validate_query_fields,
validate_feature_query_fields, validate_filter_parameters,
validate_separator)
validate_separator, validate_secret_identifier)


def load_arguments(self, _):
Expand Down Expand Up @@ -127,6 +127,12 @@ def load_arguments(self, _):
c.argument('content_type', help='Content type of the keyvalue to be set.')
c.argument('value', help='Value of the keyvalue to be set.')

with self.argument_context('appconfig kv set-keyvault') as c:
c.argument('key', help='Key to be set.')
c.argument('label', help="If no label specified, set the key with null label by default")
c.argument('tags', arg_type=tags_type)
c.argument('secret_identifier', validator=validate_secret_identifier, help="ID of the Key Vault object. Can be found using 'az keyvault {collection} show' command, where collection is key, secret or certificate. To set reference to the latest version of your secret, remove version information from secret identifier.")

with self.argument_context('appconfig kv delete') as c:
c.argument('key', help='Support star sign as filters, for instance * means all key and abc* means keys with abc as prefix. Similarly, *abc and *abc* are also supported.')
c.argument('label', help="If no label specified, delete entry with null label. Support star sign as filters, for instance * means all label and abc* means labels with abc as prefix. Similarly, *abc and *abc* are also supported.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,15 @@ def validate_filter_parameter(string):
else:
logger.warning("Ignoring filter parameter '%s' because parameter name is empty.", string)
return result


def validate_secret_identifier(namespace):
""" Validate the format of keyvault reference secret identifier """
from azure.keyvault.key_vault_id import KeyVaultIdentifier

identifier = getattr(namespace, 'secret_identifier', None)
try:
# this throws an exception for invalid format of secret identifier
KeyVaultIdentifier(uri=identifier)
except Exception as e:
Copy link
Contributor

@shenmuxiaosen shenmuxiaosen Dec 16, 2019

Choose a reason for hiding this comment

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

Exception [](start = 11, length = 9)

Any specific type of exception this can throw? Since our error message is very specific to secret identifier value. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

Going through the keyvault SDK, it looks like they only throw ValueError and TypeError at this time. But I didn't find any official documentation which says they can't throw anything else in future. I could change our error message to clarify that the exception was thrown by keyvault.


In reply to: 358527193 [](ancestors = 358527193)

raise CLIError("Received an exception while validating the format of secret identifier.\n{0}".format(str(e)))
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def get_custom_sdk(custom_module, client_factory, table_transformer):
g.command('restore', 'restore_key')
g.command('import', 'import_config')
g.command('export', 'export_config')
g.command('set-keyvault', 'set_keyvault')

# FeatureManagement Commands
with self.command_group('appconfig feature',
Expand Down
60 changes: 60 additions & 0 deletions src/azure-cli/azure/cli/command_modules/appconfig/keyvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
logger = get_logger(__name__)
FEATURE_FLAG_PREFIX = ".appconfig.featureflag/"
FEATURE_FLAG_CONTENT_TYPE = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"
KEYVAULT_CONTENT_TYPE = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"


def import_config(cmd,
Expand Down Expand Up @@ -273,6 +274,65 @@ def set_key(cmd,
"Fail to set the key '{}' due to a conflicting operation.".format(key))


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 = AzconfigClient(connection_string)

keyvault_ref_value = json.dumps({"uri": secret_identifier}, ensure_ascii=False)
retry_times = 3
Copy link
Contributor

@shenmuxiaosen shenmuxiaosen Dec 16, 2019

Choose a reason for hiding this comment

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

why we need json dumps here to create a dictionary? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

'value' attribute of KeyValue object is supposed to be a string. In order to preserve double quotes while converting this dict to str, we use json dumps.


In reply to: 358477565 [](ancestors = 358477565)

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, so the key value will look like
{
"Key":"K1"
"Value":"{"uri":"identitfier"}"
} ?


In reply to: 358518179 [](ancestors = 358518179,358477565)

Copy link
Member Author

@avanigupta avanigupta Dec 17, 2019

Choose a reason for hiding this comment

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

Yes, only the double quotes around uri and identifier will be escaped.


In reply to: 358528290 [](ancestors = 358528290,358518179,358477565)

retry_interval = 1

label = label if label and label != ModifyKeyValueOptions.empty_label else None
for i in range(0, retry_times):
try:
retrieved_kv = azconfig_client.get_keyvalue(key, QueryKeyValueOptions(label))
except HTTPException as exception:
raise CLIError(str(exception))

if retrieved_kv is None:
set_kv = KeyValue(key, keyvault_ref_value, label, tags, KEYVAULT_CONTENT_TYPE)
else:
logger.warning("This operation will result in overwriting existing key whose value is: %s", retrieved_kv.value)
set_kv = KeyValue(key=key,
label=label,
value=keyvault_ref_value,
content_type=KEYVAULT_CONTENT_TYPE,
tags=retrieved_kv.tags if tags is None else tags)
set_kv.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"
Copy link
Contributor

@shenmuxiaosen shenmuxiaosen Dec 16, 2019

Choose a reason for hiding this comment

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

add ensure_ascii=False in json.dumps #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

done


In reply to: 358478003 [](ancestors = 358478003)

user_confirmation(confirmation_message, yes)

try:
return azconfig_client.add_keyvalue(set_kv, ModifyKeyValueOptions()) if set_kv.etag is None else azconfig_client.update_keyvalue(set_kv, ModifyKeyValueOptions())
except HTTPException as exception:
if exception.status == 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,
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ interactions:
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002?api-version=2019-10-01
response:
body:
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Creating","creationDate":"2019-12-12T18:59:57.6730773+00:00","endpoint":"https://MgmtTest000002.azconfig.io"},"sku":null,"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002","name":"MgmtTest000002","location":"eastus","tags":{}}'
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Creating","creationDate":"2019-12-17T02:19:23.573121+00:00","endpoint":"https://MgmtTest000002.azconfig.io"},"sku":null,"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002","name":"MgmtTest000002","location":"eastus","tags":{}}'
headers:
azure-asyncoperation:
- https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/7d5cfa37-5d02-abd1-e0c0-1782038f539e?api-version=2019-10-01
- https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/55730fe2-db0c-cc8e-201f-d05eedfd2847?api-version=2019-10-01
cache-control:
- no-cache
content-length:
- '519'
- '518'
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 18:59:57 GMT
- Tue, 17 Dec 2019 02:19:23 GMT
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -69,21 +69,21 @@ interactions:
- python/3.7.4 (Windows-10-10.0.18362-SP0) msrest/0.6.10 msrest_azure/0.6.2
azure-mgmt-appconfiguration/0.3.0 Azure-SDK-For-Python AZURECLI/2.0.77
method: GET
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/7d5cfa37-5d02-abd1-e0c0-1782038f539e?api-version=2019-10-01
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/55730fe2-db0c-cc8e-201f-d05eedfd2847?api-version=2019-10-01
response:
body:
string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/7d5cfa37-5d02-abd1-e0c0-1782038f539e","name":"7d5cfa37-5d02-abd1-e0c0-1782038f539e","status":"Succeeded","error":null}'
string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/55730fe2-db0c-cc8e-201f-d05eedfd2847","name":"55730fe2-db0c-cc8e-201f-d05eedfd2847","status":"Succeeded","error":null}'
headers:
azure-asyncoperation:
- https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/7d5cfa37-5d02-abd1-e0c0-1782038f539e?api-version=2019-10-01
- https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/eastus/operationsStatus/55730fe2-db0c-cc8e-201f-d05eedfd2847?api-version=2019-10-01
cache-control:
- no-cache
content-length:
- '248'
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:07 GMT
- Tue, 17 Dec 2019 02:19:34 GMT
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -121,7 +121,7 @@ interactions:
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002?api-version=2019-10-01
response:
body:
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-12T18:59:59+00:00","endpoint":"https://mgmttesthpcjncj3qtwx3u7k.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttesthpcjncj3qtwx3u7k","name":"MgmtTest000002","location":"eastus","tags":{}}'
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-17T02:19:25+00:00","endpoint":"https://mgmttest5briudbn7y3j3362.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttest5briudbn7y3j3362","name":"MgmtTest000002","location":"eastus","tags":{}}'
headers:
cache-control:
- no-cache
Expand All @@ -130,9 +130,9 @@ interactions:
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:08 GMT
- Tue, 17 Dec 2019 02:19:34 GMT
etag:
- W/"450080c1-0000-0100-0000-5df28e2f0000"
- W/"00003b01-0000-0100-0000-5df83b2c0000"
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -172,7 +172,7 @@ interactions:
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01
response:
body:
string: '{"value":[{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-12T18:59:59+00:00","endpoint":"https://mgmttesthpcjncj3qtwx3u7k.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttesthpcjncj3qtwx3u7k","name":"MgmtTest000002","location":"eastus","tags":{}}],"nextLink":"https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttesthpcjncj3qtwx3u7k"}'
string: '{"value":[{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-17T02:19:25+00:00","endpoint":"https://mgmttest5briudbn7y3j3362.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttest5briudbn7y3j3362","name":"MgmtTest000002","location":"eastus","tags":{}}],"nextLink":"https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttest5briudbn7y3j3362"}'
headers:
cache-control:
- no-cache
Expand All @@ -181,11 +181,11 @@ interactions:
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:09 GMT
- Tue, 17 Dec 2019 02:19:35 GMT
expires:
- '-1'
link:
- </subscriptions/77d5ab06-7edd-4eec-bce9-52c11b75bb37/resourceGroups/clitest.rgmtgv5rmftroi5frnii3tm4n64p6vl6khetgw6xnf7oociw6w6qixejvs4dyjuccec/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttesthpcjncj3qtwx3u7k>;
- </subscriptions/77d5ab06-7edd-4eec-bce9-52c11b75bb37/resourceGroups/clitest.rgrvxgvpypx3x4g5uycpeha73nzrozvth6mxxsrtmcoybk6oy6hrobppzklexjehe44/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttest5briudbn7y3j3362>;
rel="next"
pragma:
- no-cache
Expand Down Expand Up @@ -221,7 +221,7 @@ interactions:
accept-language:
- en-US
method: GET
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttesthpcjncj3qtwx3u7k
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores?api-version=2019-10-01&$skipToken=mgmttest5briudbn7y3j3362
response:
body:
string: '{"value":[],"nextLink":null}'
Expand All @@ -233,7 +233,7 @@ interactions:
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:09 GMT
- Tue, 17 Dec 2019 02:19:35 GMT
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -273,7 +273,7 @@ interactions:
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002?api-version=2019-10-01
response:
body:
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-12T18:59:59+00:00","endpoint":"https://mgmttesthpcjncj3qtwx3u7k.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttesthpcjncj3qtwx3u7k","name":"MgmtTest000002","location":"eastus","tags":{}}'
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-17T02:19:25+00:00","endpoint":"https://mgmttest5briudbn7y3j3362.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttest5briudbn7y3j3362","name":"MgmtTest000002","location":"eastus","tags":{}}'
headers:
cache-control:
- no-cache
Expand All @@ -282,9 +282,9 @@ interactions:
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:10 GMT
- Tue, 17 Dec 2019 02:19:35 GMT
etag:
- W/"450080c1-0000-0100-0000-5df28e2f0000"
- W/"00003b01-0000-0100-0000-5df83b2c0000"
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -328,7 +328,7 @@ interactions:
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/MgmtTest000002?api-version=2019-10-01
response:
body:
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-12T18:59:59+00:00","endpoint":"https://mgmttesthpcjncj3qtwx3u7k.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttesthpcjncj3qtwx3u7k","name":"MgmtTest000002","location":"eastus","tags":{"Env":"Prod"}}'
string: '{"type":"Microsoft.AppConfiguration/configurationStores","properties":{"provisioningState":"Succeeded","creationDate":"2019-12-17T02:19:25+00:00","endpoint":"https://mgmttest5briudbn7y3j3362.azconfig.io"},"sku":{"name":"free"},"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.AppConfiguration/configurationStores/mgmttest5briudbn7y3j3362","name":"MgmtTest000002","location":"eastus","tags":{"Env":"Prod"}}'
headers:
cache-control:
- no-cache
Expand All @@ -337,9 +337,9 @@ interactions:
content-type:
- application/json; charset=utf-8
date:
- Thu, 12 Dec 2019 19:00:10 GMT
- Tue, 17 Dec 2019 02:19:36 GMT
etag:
- W/"450082c1-0000-0100-0000-5df28e3b0000"
- W/"00004201-0000-0100-0000-5df83b390000"
expires:
- '-1'
pragma:
Expand Down Expand Up @@ -390,7 +390,7 @@ interactions:
content-length:
- '0'
date:
- Thu, 12 Dec 2019 19:00:12 GMT
- Tue, 17 Dec 2019 02:19:38 GMT
expires:
- '-1'
pragma:
Expand Down
Loading