From 7d04d3254a9596adad767b780b560045eba79c92 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 26 Sep 2022 10:02:07 -0700 Subject: [PATCH] Test-AzTemplate: Skipping nested templates for prereqs (#690) * Test-AzTemplate: Skipping nested templates for prereq,parameters, and CreateUIDefinition (because they cannot have them) (Fixes #686, makes #680 more quiet) * Adding Test Directory for JSONFiles-Should-Be-Valid (re #686) * Delete prereq.azuredeploy.parameters.json * Delete azuredeploy.parameters.json * Delete .settings.json Co-authored-by: James Brundage <@github.com> Co-authored-by: Brian Moore --- arm-ttk/Test-AzTemplate.ps1 | 16 +- .../JSONFiles-Should-Be-Valid.tests.ps1 | 6 + .../Pass/azuredeploy.json | 338 ++++++++++++++++++ .../Pass/prereqs/prereq.azuredeploy.json | 219 ++++++++++++ 4 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 unit-tests/JSONFiles-Should-Be-Valid/JSONFiles-Should-Be-Valid.tests.ps1 create mode 100644 unit-tests/JSONFiles-Should-Be-Valid/Pass/azuredeploy.json create mode 100644 unit-tests/JSONFiles-Should-Be-Valid/Pass/prereqs/prereq.azuredeploy.json diff --git a/arm-ttk/Test-AzTemplate.ps1 b/arm-ttk/Test-AzTemplate.ps1 index 7e93fc3f..150aacdb 100644 --- a/arm-ttk/Test-AzTemplate.ps1 +++ b/arm-ttk/Test-AzTemplate.ps1 @@ -424,11 +424,13 @@ Each test script has access to a set of well-known variables: } } - if ($testOut.InnerTemplateLocation) { + if ($testOut.InnerTemplateLocation -and $location) { $location.Line += $testOut.InnerTemplateLocation.Line - 1 } - $testOut | Add-Member NoteProperty Location $location -Force + if ($location) { + $testOut | Add-Member NoteProperty Location $location -Force + } } } elseif ($testOut -is [Management.Automation.WarningRecord]) { @@ -565,12 +567,20 @@ Each test script has access to a set of well-known variables: $templateFileName = $fileInfo.Name $TemplateObject = $fileInfo.Object $TemplateText = $fileInfo.Text + # If the file had inner templates if ($fileInfo.InnerTemplates) { + # use the inner templates from just this file $InnerTemplates = $fileInfo.InnerTemplates $InnerTemplatesText = $fileInfo.InnerTemplatesText $InnerTemplatesNames = $fileInfo.InnerTemplatesNames $innerTemplatesLocations = $fileInfo.InnerTemplatesLocations - } else { + } + elseif ($fileInfo.Name -match '^(?>parameters|prereq|CreateUIDefinition)\.') { + $InnerTemplates, $InnerTemplateText, $InnerTemplatesNames, $innerTemplatesLocations = $null + } + else + { + # Otherwise, use the inner templates from the main file $InnerTemplates = $mainInnerTemplates $InnerTemplatesText = $mainInnerTemplatesText $InnerTemplatesNames = $MainInnerTemplatesNames diff --git a/unit-tests/JSONFiles-Should-Be-Valid/JSONFiles-Should-Be-Valid.tests.ps1 b/unit-tests/JSONFiles-Should-Be-Valid/JSONFiles-Should-Be-Valid.tests.ps1 new file mode 100644 index 00000000..d9e0b2ec --- /dev/null +++ b/unit-tests/JSONFiles-Should-Be-Valid/JSONFiles-Should-Be-Valid.tests.ps1 @@ -0,0 +1,6 @@ + +#requires -module arm-ttk +. $PSScriptRoot\..\arm-ttk.test.functions.ps1 +Test-TTK $psScriptRoot +return + diff --git a/unit-tests/JSONFiles-Should-Be-Valid/Pass/azuredeploy.json b/unit-tests/JSONFiles-Should-Be-Valid/Pass/azuredeploy.json new file mode 100644 index 00000000..3a766eba --- /dev/null +++ b/unit-tests/JSONFiles-Should-Be-Valid/Pass/azuredeploy.json @@ -0,0 +1,338 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.10.61.36676", + "templateHash": "4722508883802150279" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location for all resources." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning workspace where sweep job will be deployed" + } + }, + "jobName": { + "type": "string", + "metadata": { + "description": "Specifies the unique name for sweep job." + } + }, + "computeName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning amlcompute cluster on which job will be run." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "The name for the storage account to created and associated with the workspace." + } + }, + "experimentName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning experiment under which job will be created." + } + }, + "_artifactsLocation": { + "type": "string", + "defaultValue": "[deployment().properties.templateLink.uri]", + "metadata": { + "description": "The base URI where artifacts required by this template are located including a trailing '/'." + } + }, + "_artifactsLocationSasToken": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "The sasToken required to access _artifactsLocation." + } + }, + "inputs": { + "type": "object", + "defaultValue": { + "iris_csv": { + "mode": "ReadOnlyMount", + "uri": "[uri(parameters('_artifactsLocation'), format('data/iris.csv{0}', parameters('_artifactsLocationSasToken')))]", + "jobInputType": "uri_file" + } + }, + "metadata": { + "description": "Specifies dictionary of inputs search for sweep job." + } + }, + "limits": { + "type": "object", + "defaultValue": { + "jobLimitsType": "Sweep", + "timeout": "PT20M", + "trialTimeout": "PT50S", + "maxConcurrentTrials": 3, + "maxTotalTrials": 5 + }, + "metadata": { + "description": "Specifies execution contraints for sweep job." + } + }, + "objective": { + "type": "object", + "defaultValue": { + "goal": "maximize", + "primaryMetric": "result" + }, + "metadata": { + "description": "Specifies objective for sweep job." + } + }, + "samplingAlgorithmType": { + "type": "string", + "defaultValue": "Random", + "metadata": { + "description": "Specifies sampling algorithm for sweep job." + } + }, + "searchSpace": { + "type": "object", + "defaultValue": { + "learning_rate": [ + "uniform", + [ + "[json('0.01')]", + "[json('0.9')]" + ] + ], + "boosting": [ + "choice", + [ + [ + "gbdt", + "dart" + ] + ] + ] + }, + "metadata": { + "description": "Specifies different search space for sweep job." + } + }, + "command": { + "type": "string", + "defaultValue": "python main.py --iris-csv ${{inputs.iris_csv}} --learning-rate ${{search_space.learning_rate}} --boosting ${{search_space.boosting}}", + "metadata": { + "description": "Specifies command to be executed by trials of sweep job." + } + }, + "environmentName": { + "type": "string", + "defaultValue": "AzureML-lightgbm-3.2-ubuntu18.04-py37-cpu", + "metadata": { + "description": "Specifies the curated environment to run sweep job." + } + } + }, + "resources": [ + { + "type": "Microsoft.MachineLearningServices/workspaces/jobs", + "apiVersion": "2022-06-01-preview", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('jobName'))]", + "properties": { + "description": "Sweep Job Resource from ARM Template", + "properties": {}, + "tags": { + "referenceNotebook": "https://github.com/Azure/azureml-examples/blob/main/sdk/jobs/single-step/lightgbm/iris/lightgbm-iris-sweep.ipynb" + }, + "computeId": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('workspaceName'), parameters('computeName'))]", + "displayName": "Sweep Job Resource", + "experimentName": "[parameters('experimentName')]", + "isArchived": false, + "jobType": "Sweep", + "inputs": "[parameters('inputs')]", + "limits": "[parameters('limits')]", + "objective": "[parameters('objective')]", + "samplingAlgorithm": { + "samplingAlgorithmType": "[parameters('samplingAlgorithmType')]" + }, + "searchSpace": "[parameters('searchSpace')]", + "trial": { + "codeId": "[reference(resourceId('Microsoft.Resources/deployments', 'blob')).outputs.codeId.value]", + "command": "[parameters('command')]", + "environmentId": "[resourceId('Microsoft.MachineLearningServices/workspaces/environments/versions', parameters('workspaceName'), parameters('environmentName'), reference(resourceId('Microsoft.MachineLearningServices/workspaces/environments', split(format('{0}/{1}', parameters('workspaceName'), parameters('environmentName')), '/')[0], split(format('{0}/{1}', parameters('workspaceName'), parameters('environmentName')), '/')[1]), '2022-05-01').latestVersion)]", + "environmentVariables": {} + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'blob')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "blob", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "workspaceName": { + "value": "[parameters('workspaceName')]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.10.61.36676", + "templateHash": "17993837818224864413" + } + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning workspace where sweep job will be deployed" + } + }, + "filename": { + "type": "string", + "defaultValue": "main.py", + "metadata": { + "description": "Name of the blob as it is stored in the blob container" + } + }, + "containerName": { + "type": "string", + "defaultValue": "hdscript", + "metadata": { + "description": "Name of the blob container" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region where resources should be deployed" + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Desired name of the storage account" + } + }, + "codeVersion": { + "type": "string", + "defaultValue": "1", + "metadata": { + "description": "Specifies the env version for sweep job." + } + }, + "codeId": { + "type": "string", + "defaultValue": "code", + "metadata": { + "description": "Specifies the env for sweep job." + } + } + }, + "variables": { + "$fxv#0": "# imports\r\nimport os\r\nimport mlflow\r\nimport argparse\r\n\r\nimport pandas as pd\r\nimport lightgbm as lgbm\r\nimport matplotlib.pyplot as plt\r\n\r\nfrom sklearn.metrics import log_loss, accuracy_score\r\nfrom sklearn.preprocessing import LabelEncoder\r\nfrom sklearn.model_selection import train_test_split\r\n\r\n# define functions\r\ndef main(args):\r\n # enable auto logging\r\n mlflow.autolog()\r\n\r\n # setup parameters\r\n num_boost_round = args.num_boost_round\r\n params = {\r\n \"objective\": \"multiclass\",\r\n \"num_class\": 3,\r\n \"boosting\": args.boosting,\r\n \"num_iterations\": args.num_iterations,\r\n \"num_leaves\": args.num_leaves,\r\n \"num_threads\": args.num_threads,\r\n \"learning_rate\": args.learning_rate,\r\n \"metric\": args.metric,\r\n \"seed\": args.seed,\r\n \"verbose\": args.verbose,\r\n }\r\n\r\n # read in data\r\n df = pd.read_csv(args.iris_csv)\r\n\r\n # process data\r\n X_train, X_test, y_train, y_test, enc = process_data(df)\r\n\r\n # train model\r\n model = train_model(params, num_boost_round, X_train, X_test, y_train, y_test)\r\n\r\n\r\ndef process_data(df):\r\n # split dataframe into X and y\r\n X = df.drop([\"species\"], axis=1)\r\n y = df[\"species\"]\r\n\r\n # encode label\r\n enc = LabelEncoder()\r\n y = enc.fit_transform(y)\r\n\r\n # train/test split\r\n X_train, X_test, y_train, y_test = train_test_split(\r\n X, y, test_size=0.2, random_state=42\r\n )\r\n\r\n # return splits and encoder\r\n return X_train, X_test, y_train, y_test, enc\r\n\r\n\r\ndef train_model(params, num_boost_round, X_train, X_test, y_train, y_test):\r\n # create lightgbm datasets\r\n train_data = lgbm.Dataset(X_train, label=y_train)\r\n test_data = lgbm.Dataset(X_test, label=y_test)\r\n\r\n # train model\r\n model = lgbm.train(\r\n params,\r\n train_data,\r\n num_boost_round=num_boost_round,\r\n valid_sets=[test_data],\r\n valid_names=[\"test\"],\r\n )\r\n\r\n # return model\r\n return model\r\n\r\n\r\ndef parse_args():\r\n # setup arg parser\r\n parser = argparse.ArgumentParser()\r\n\r\n # add arguments\r\n parser.add_argument(\"--iris-csv\", type=str)\r\n parser.add_argument(\"--num-boost-round\", type=int, default=10)\r\n parser.add_argument(\"--boosting\", type=str, default=\"gbdt\")\r\n parser.add_argument(\"--num-iterations\", type=int, default=16)\r\n parser.add_argument(\"--num-leaves\", type=int, default=31)\r\n parser.add_argument(\"--num-threads\", type=int, default=0)\r\n parser.add_argument(\"--learning-rate\", type=float, default=0.1)\r\n parser.add_argument(\"--metric\", type=str, default=\"multi_logloss\")\r\n parser.add_argument(\"--seed\", type=int, default=42)\r\n parser.add_argument(\"--verbose\", type=int, default=0)\r\n\r\n # parse args\r\n args = parser.parse_args()\r\n\r\n # return args\r\n return args\r\n\r\n\r\n# run script\r\nif __name__ == \"__main__\":\r\n # parse args\r\n args = parse_args()\r\n\r\n # run main function\r\n main(args)" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2021-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('containerName'))]", + "properties": { + "publicAccess": "Container" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('deployscript-upload-blob-{0}', uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))))]", + "location": "[parameters('location')]", + "kind": "AzureCLI", + "properties": { + "azCliVersion": "2.26.1", + "timeout": "PT5M", + "retentionInterval": "PT1H", + "environmentVariables": [ + { + "name": "AZURE_STORAGE_ACCOUNT", + "value": "[parameters('storageAccountName')]" + }, + { + "name": "AZURE_STORAGE_KEY", + "secureValue": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-04-01').keys[0].value]" + }, + { + "name": "CONTENT", + "value": "[variables('$fxv#0')]" + } + ], + "scriptContent": "[format('echo \"$CONTENT\" > {0} && az storage blob upload -f {1} -c {2} -n {3}', parameters('filename'), parameters('filename'), parameters('containerName'), parameters('filename'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))]" + ] + }, + { + "type": "Microsoft.MachineLearningServices/workspaces/codes/versions", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}-{2}/{3}', parameters('workspaceName'), parameters('codeId'), uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))), parameters('codeVersion'))]", + "properties": { + "codeUri": "[uri(format('https://{0}.blob.{1}/', parameters('storageAccountName'), environment().suffixes.storage), format('{0}/', parameters('containerName')))]", + "isAnonymous": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))]", + "[resourceId('Microsoft.Resources/deploymentScripts', format('deployscript-upload-blob-{0}', uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName')))))]" + ] + } + ], + "outputs": { + "codeId": { + "type": "string", + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/codes/versions', split(format('{0}/{1}-{2}/{3}', parameters('workspaceName'), parameters('codeId'), uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))), parameters('codeVersion')), '/')[0], split(format('{0}/{1}-{2}/{3}', parameters('workspaceName'), parameters('codeId'), uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))), parameters('codeVersion')), '/')[1], split(format('{0}/{1}-{2}/{3}', parameters('workspaceName'), parameters('codeId'), uniqueString(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('containerName'))), parameters('codeVersion')), '/')[2])]" + } + } + } + } + } + ], + "outputs": { + "Job_Studio_Endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.MachineLearningServices/workspaces/jobs', split(format('{0}/{1}', parameters('workspaceName'), parameters('jobName')), '/')[0], split(format('{0}/{1}', parameters('workspaceName'), parameters('jobName')), '/')[1])).services.Studio.endpoint]" + } + } +} \ No newline at end of file diff --git a/unit-tests/JSONFiles-Should-Be-Valid/Pass/prereqs/prereq.azuredeploy.json b/unit-tests/JSONFiles-Should-Be-Valid/Pass/prereqs/prereq.azuredeploy.json new file mode 100644 index 00000000..89d9cc9d --- /dev/null +++ b/unit-tests/JSONFiles-Should-Be-Valid/Pass/prereqs/prereq.azuredeploy.json @@ -0,0 +1,219 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location for all resources." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "The name for the storage account to created and associated with the workspace." + } + }, + "keyVaultName": { + "type": "string", + "defaultValue": "[concat('kv',uniqueString(resourceGroup().id))]", + "metadata": { + "description": "The name for the key vault to created and associated with the workspace." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[subscription().tenantId]", + "metadata": { + "description": "Specifies the tenant ID of the subscription. Get using Get-AzureRmSubscription cmdlet or Get Subscription API." + } + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "[concat('ai',uniqueString(resourceGroup().id))]", + "metadata": { + "description": "The name for the application insights to created and associated with the workspace." + } + }, + "computeName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Azure Machine Learning amlcompute clsuter to be deployed" + } + }, + "vmSize": { + "defaultValue": "Standard_DS3_v2", + "type": "string", + "metadata": { + "description": "The VM size for compute instance" + } + }, + "vnetResourceGroupName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Name of the resource group which holds the VNET to which you want to inject your compute instance in." + } + }, + "vnetName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Name of the vnet which you want to inject your compute instance in." + } + }, + "subnetName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Name of the subnet inside the VNET which you want to inject your compute instance in." + } + }, + "minNodeCount": { + "defaultValue": 0, + "type": "int", + "metadata": { + "description": "The minimum number of nodes to use on the cluster. If not specified, defaults to 0" + } + }, + "maxNodeCount": { + "defaultValue": 1, + "type": "int", + "metadata": { + "description": " The maximum number of nodes to use on the cluster. If not specified, defaults to 4." + } + }, + "adminUserName": { + "type": "securestring", + "metadata": { + "description": "The name of the administrator user account which can be used to SSH into nodes. It must only contain lower case alphabetic characters [a-z]." + } + }, + "adminUserPassword": { + "type": "securestring", + "metadata": { + "description": "The password of the administrator user account." + } + } + }, + "variables": { + "subnet": { + "id": "[resourceId(parameters('vnetResourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]" + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "name": "[parameters('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "properties": { + "encryption": { + "services": { + "blob": { + "enabled": true + }, + "file": { + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + }, + "supportsHttpsTrafficOnly": true + } + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2021-10-01", + "name": "[parameters('keyVaultName')]", + "location": "[parameters('location')]", + "properties": { + "tenantId": "[parameters('tenantId')]", + "sku": { + "name": "standard", + "family": "A" + }, + "accessPolicies": [] + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('applicationInsightsName')]", + "location": "[parameters('location')]", + "kind": "web", + "properties": { + "Application_Type": "web" + } + }, + { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2022-05-01", + "name": "[parameters('workspaceName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]", + "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ], + "identity": { + "type": "systemAssigned" + }, + "properties": { + "friendlyName": "[parameters('workspaceName')]", + "storageAccount": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]", + "keyVault": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]", + "applicationInsights": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + } + }, + { + "type": "Microsoft.MachineLearningServices/workspaces/computes", + "name": "[concat(parameters('workspaceName'), '/', parameters('computeName'))]", + "apiVersion": "2021-01-01", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('workspaceName'))]" + ], + "properties": { + "computeType": "AmlCompute", + "properties": { + "vmSize": "[parameters('vmSize')]", + "scaleSettings": { + "minNodeCount": "[parameters('minNodeCount')]", + "maxNodeCount": "[parameters('maxNodeCount')]" + }, + "userAccountCredentials": { + "adminUserName": "[parameters('adminUserName')]", + "adminUserPassword": "[parameters('adminUserPassword')]" + }, + "subnet": "[if(and(not(empty(parameters('vnetResourceGroupName'))),not(empty(parameters('vnetName'))),not(empty(parameters('subnetName')))), variables('subnet'), json('null'))]" + } + } + } + ], + "outputs": { + "workspaceName": { + "type": "string", + "value": "[parameters('workspaceName')]" + }, + "computeName": { + "type": "string", + "value": "[parameters('computeName')]" + }, + "storageAccountName": { + "type": "string", + "value": "[parameters('storageAccountName')]" + } + } +} \ No newline at end of file