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
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