Skip to content

Commit

Permalink
[ARM] Az Deployments : Add support for --query-string (Azure#16447)
Browse files Browse the repository at this point in the history
* Query string support

* Try catch added for query_string

* Improve error messages

Co-authored-by: Dania Etienne <daetienn@microsoft.com>
  • Loading branch information
detienne20 and Dania Etienne authored Jan 11, 2021
1 parent 9601031 commit c66daef
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 44 deletions.
2 changes: 2 additions & 0 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def load_arguments(self, _):
help="a template file path in the file system")
deployment_template_uri_type = CLIArgumentType(options_list=['--template-uri', '-u'], help='a uri to a remote template file')
deployment_template_spec_type = CLIArgumentType(options_list=['--template-spec', '-s'], is_preview=True, min_api='2019-06-01', help="The template spec resource id.")
deployment_query_string_type = CLIArgumentType(options_list=['--query-string', '-q'], is_preview=True, help="The query string (a SAS token) to be used with the template-uri in the case of linked templates.")
deployment_parameters_type = CLIArgumentType(options_list=['--parameters', '-p'], action='append', nargs='+', completer=FilesCompleter(), help='the deployment parameters')
filter_type = CLIArgumentType(options_list=['--filter'], is_preview=True,
help='Filter expression using OData notation. You can use --filter "provisioningState eq \'{state}\'" to filter provisioningState. '
Expand Down Expand Up @@ -262,6 +263,7 @@ def load_arguments(self, _):
c.argument('template_file', arg_type=deployment_template_file_type)
c.argument('template_uri', arg_type=deployment_template_uri_type)
c.argument('template_spec', arg_type=deployment_template_spec_type)
c.argument('query_string', arg_type=deployment_query_string_type)
c.argument('parameters', arg_type=deployment_parameters_type)

with self.argument_context('deployment create') as c:
Expand Down
109 changes: 67 additions & 42 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-10-01",
"apiVersion": "2020-10-01",
"name": "keyVault",
"properties": {
"mode": "Incremental",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"projectName": {
"type": "string",
"minLength": 3,
"maxLength": 11,
"metadata": {
"description": "Specify a project name that is used to generate resource names."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Specify a location for the resources."
}
},
"linuxFxVersion": {
"type": "string",
"defaultValue": "php|7.0",
"metadata": {
"description": "The Runtime stack of current web app"
}
}
},
"variables": {
"storageAccountName": "[concat(parameters('projectName'), 'store')]",
"webAppName": "[concat(parameters('projectName'), 'WebApp')]",
"appServicePlanName": "[concat(parameters('projectName'), 'Plan')]"
},
"resources": [
{
"name": "storage_account_linked_template",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"relativePath":"storage_account_linked_template.json",
"contentVersion":"1.0.0.0"
},
"parameters": {
"storageAccountName": {
"value": "[variables('storageAccountName')]"
},
"location": {
"value": "[parameters('location')]"
}
}
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2016-09-01",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "B1",
"tier": "Basic",
"size": "B1",
"family": "B",
"capacity": 1
},
"kind": "linux",
"properties": {
"perSiteScaling": false,
"reserved": true,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"name": "[variables('webAppName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
],
"kind": "app",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
"siteConfig": {
"linuxFxVersion": "[parameters('linuxFxVersion')]"
}
}
}
],
"outputs": {
"storageEndpoint": {
"type": "object",
"value": "[reference('storage_account_linked_template').outputs.storageEndpoint.value]"
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "Specify the storage account name."
}
},
"location": {
"type": "string",
"metadata": {
"description": "Specify a location for the resources."
}
},
"storageSKU": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS",
"Standard_ZRS",
"Premium_LRS",
"Premium_ZRS",
"Standard_GZRS",
"Standard_RAGZRS"
],
"metadata": {
"description": "Specify the storage account type."
}
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageSKU')]"
},
"kind": "StorageV2",
"properties": {
"supportsHttpsTrafficOnly": true
}
}
],
"outputs": {
"storageEndpoint": {
"type": "object",
"value": "[reference(parameters('storageAccountName')).primaryEndpoints]"
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
/*
comment
*/
"parameters": {
"rgName": {
"type": "string"
},
"rgLocation": {
"type": "string"
},
"keyVaultName": {
"type": "string"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "deploymentRg",
"location": "[parameters('rgLocation')]",
"properties": {
"mode": "Incremental",
"templateLink": {
"relativePath":"createResourceGroup.json",
"contentVersion":"1.0.0.0"
},
"parameters": {
"rgName":{"value": "[parameters('rgName')]"},
"rgLocation":{"value": "[parameters('rgLocation')]"}
}
}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "keyVaultAndSecret",
"resourceGroup": "[parameters('rgName')]",
"dependsOn": [
"deploymentRg"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"relativePath":"createKeyVaultWithSecret.json",
"contentVersion":"1.0.0.0"
},
"parameters": {
"keyVaultName":{"value": "[parameters('keyVaultName')]"}
}
}
}
],
"outputs": {}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from azure.cli.core.parser import IncorrectUsageError
from azure_devtools.scenario_tests.const import MOCKED_SUBSCRIPTION_ID
from azure_devtools.scenario_tests import AllowLargeResponse
from azure.cli.testsdk import (ScenarioTest, LocalContextScenarioTest, LiveScenarioTest, ResourceGroupPreparer,
from azure.cli.testsdk import (ScenarioTest, LocalContextScenarioTest, LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer,
create_random_name, live_only, record_only)
from azure.cli.core.util import get_file_json
from knack.util import CLIError
Expand Down Expand Up @@ -1012,6 +1012,99 @@ def test_export_template_spec(self, resource_group, resource_group_location):



class DeploymentTestsWithQueryString(LiveScenarioTest):
@ResourceGroupPreparer(name_prefix='cli_test_query_str_rg', location='eastus')
@StorageAccountPreparer(name_prefix='testquerystrrg', location='eastus', kind='StorageV2')
def test_resource_group_level_deployment_with_query_string(self, resource_group, resource_group_location, storage_account):

container_name = self.create_random_name('querystr', 20)
curr_dir = os.path.dirname(os.path.realpath(__file__))
tf = os.path.join(curr_dir, 'resource_group_level_linked_template.json')
linked_template = os.path.join(curr_dir, 'storage_account_linked_template.json')

self.kwargs.update({
'resource_group': resource_group,
'storage_account': storage_account,
'container_name': container_name,
'tf': tf,
'linked_tf': linked_template
})

self.kwargs['storage_key'] = str(self.cmd('az storage account keys list -n {storage_account} -g {resource_group} --query "[0].value"').output)

self.cmd('storage container create -n {container_name} --account-name {storage_account} --account-key {storage_key}')

self.cmd('storage blob upload -c {container_name} -f "{tf}" -n mainTemplate --account-name {storage_account} --account-key {storage_key}')
self.cmd('storage blob upload -c {container_name} -f "{linked_tf}" -n storage_account_linked_template.json --account-name {storage_account} --account-key {storage_key}')

from datetime import datetime, timedelta
self.kwargs['expiry'] = (datetime.utcnow() + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ')

self.kwargs['sas_token'] = self.cmd(
'storage container generate-sas --account-name {storage_account} --account-key {storage_key} --name {container_name} --permissions rw --expiry {expiry} -otsv').output.strip()

self.kwargs['blob_url'] = self.cmd(
'storage blob url -c {container_name} -n mainTemplate --account-name {storage_account} --account-key {storage_key}').output.strip()

self.cmd('deployment group validate -g {resource_group} --template-uri {blob_url} --query-string "{sas_token}" --parameters projectName=qsproject', checks=[
self.check('properties.provisioningState', 'Succeeded')
])

self.cmd('deployment group create -g {resource_group} --template-uri {blob_url} --query-string "{sas_token}" --parameters projectName=qsproject', checks=[
self.check('properties.provisioningState', 'Succeeded')
])

@ResourceGroupPreparer(name_prefix='cli_test_query_str_sub', location='eastus')
@StorageAccountPreparer(name_prefix='testquerystrsub', location='eastus', kind='StorageV2')
def test_subscription_level_deployment_with_query_string(self, resource_group, resource_group_location, storage_account):

container_name = self.create_random_name('querystr', 20)
curr_dir = os.path.dirname(os.path.realpath(__file__))
tf = os.path.join(curr_dir, 'subscription_level_linked_template.json')
linked_tf = os.path.join(curr_dir, 'createResourceGroup.json')
linked_tf1 = os.path.join(curr_dir, 'createKeyVault.json')
linked_tf2 = os.path.join(curr_dir, 'createKeyVaultWithSecret.json')

self.kwargs.update({
'resource_group': resource_group,
'resource_group_location': resource_group_location,
'storage_account': storage_account,
'container_name': container_name,
'tf': tf,
'linked_tf': linked_tf,
'linked_tf1': linked_tf1,
'linked_tf2': linked_tf2
})

self.kwargs['storage_key'] = str(self.cmd('az storage account keys list -n {storage_account} -g {resource_group} --query "[0].value"').output)

self.cmd('storage container create -n {container_name} --account-name {storage_account} --account-key {storage_key}')

self.cmd('storage blob upload -c {container_name} -f "{tf}" -n mainTemplate --account-name {storage_account} --account-key {storage_key}')
self.cmd('storage blob upload -c {container_name} -f "{linked_tf}" -n createResourceGroup.json --account-name {storage_account} --account-key {storage_key}')
self.cmd('storage blob upload -c {container_name} -f "{linked_tf1}" -n createKeyVault.json --account-name {storage_account} --account-key {storage_key}')
self.cmd('storage blob upload -c {container_name} -f "{linked_tf2}" -n createKeyVaultWithSecret.json --account-name {storage_account} --account-key {storage_key}')

from datetime import datetime, timedelta
self.kwargs['expiry'] = (datetime.utcnow() + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ')

self.kwargs['sas_token'] = self.cmd(
'storage container generate-sas --account-name {storage_account} --name {container_name} --permissions dlrw --expiry {expiry} --https-only -otsv').output.strip()

self.kwargs['blob_url'] = self.cmd(
'storage blob url -c {container_name} -n mainTemplate --account-name {storage_account}').output.strip()

self.kwargs['key_vault'] = self.create_random_name('querystrKV', 20)

self.cmd('deployment sub validate -l {resource_group_location} --template-uri {blob_url} --query-string "{sas_token}" --parameters keyVaultName="{key_vault}" rgName="{resource_group}" rgLocation="{resource_group_location}"', checks=[
self.check('properties.provisioningState', 'Succeeded')
])

self.cmd('deployment sub create -l {resource_group_location} --template-uri {blob_url} --query-string "{sas_token}" --parameters keyVaultName="{key_vault}" rgName="{resource_group}" rgLocation="{resource_group_location}"', checks=[
self.check('properties.provisioningState', 'Succeeded')
])


class DeploymentTestAtSubscriptionScope(ScenarioTest):
def tearDown(self):
self.cmd('policy assignment delete -n location-lock')
Expand Down

0 comments on commit c66daef

Please sign in to comment.