Exercise - Add parameters to deployment scripts
Now that you've used a deployment script to migrate some manual work into your Azure Resource Manager (ARM) template, another partner application team in your organization has asked for some help.
The team's process has similar requirements, but the team needs to deploy multiple files to its storage account. The team has a PowerShell script that can take a list of files as a parameter and upload them, similar to the script that you were already using in your template.
In this exercise, you'll take your previous template as a starting point and update the PowerShell script to use the one from your partner team. Then, you'll add a way to enable the person who's deploying the template to specify what configuration files to deploy (one or more).
During the process, you'll:
- Update the deployment script.
- Add an environment variable and template parameter, and pass these to your deployment script.
- Add an output to the deployment script.
- Add a parameters file.
- Deploy the template, and verify the outcome.
Create the starting template
You start with the template that you created in the last exercise.
Open Visual Studio Code, and create a new file called azuredeploy.json.
Copy the following starting template into azuredeploy.json.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.1", "apiProfile": "", "parameters": {}, "variables": { "storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]", "storageBlobContainerName": "config", "userAssignedIdentityName": "configDeployer", "roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]", "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "deploymentScriptName": "CopyConfigScript" }, "functions": [], "resources": [ { "name": "[variables('storageAccountName')]", "type": "Microsoft.Storage/storageAccounts", "apiVersion": "2023-01-01", "tags": { "displayName": "[variables('storageAccountName')]" }, "location": "[resourceGroup().location]", "kind": "StorageV2", "sku": { "name": "Standard_LRS", "tier": "Standard" }, "properties": { "allowBlobPublicAccess": true, "encryption": { "services": { "blob": { "enabled": true } }, "keySource": "Microsoft.Storage" }, "supportsHttpsTrafficOnly": true } }, { "type": "Microsoft.Storage/storageAccounts/blobServices", "apiVersion": "2019-04-01", "name": "[concat(variables('storageAccountName'), '/default')]", "dependsOn": [ "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" ] }, { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2019-04-01", "name": "[concat(variables('storageAccountName'),'/default/',variables('storageBlobContainerName'))]", "dependsOn": [ "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]", "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" ], "properties": { "publicAccess": "Blob" } }, { "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2018-11-30", "name": "[variables('userAssignedIdentityName')]", "location": "[resourceGroup().location]" }, { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2020-04-01-preview", "name": "[variables('roleAssignmentName')]", "dependsOn": [ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]" ], "properties": { "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName')), '2015-08-31-preview').principalId]", "scope": "[resourceGroup().id]", "principalType": "ServicePrincipal" } }, { "type": "Microsoft.Resources/deploymentScripts", "apiVersion": "2020-10-01", "name": "[variables('deploymentScriptName')]", "location": "[resourceGroup().location]", "kind": "AzurePowerShell", "dependsOn": [ "[resourceId('Microsoft.Authorization/roleAssignments', variables('roleAssignmentName'))]", "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('storageAccountName'), 'default', variables('storageBlobContainerName'))]" ], "identity": { "type": "UserAssigned", "userAssignedIdentities": { "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',variables('userAssignedIdentityName'))]": {} } }, "properties": { "azPowerShellVersion": "3.0", "scriptContent": " Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/appsettings.json' -OutFile 'appsettings.json' $storageAccount = Get-AzStorageAccount -ResourceGroupName 'learndeploymentscript_exercise_1' | Where-Object { $_.StorageAccountName -like 'storage*' } $blob = Set-AzStorageBlobContent -File 'appsettings.json' -Container 'config' -Blob 'appsettings.json' -Context $StorageAccount.Context $DeploymentScriptOutputs = @{} $DeploymentScriptOutputs['Uri'] = $blob.ICloudBlob.Uri $DeploymentScriptOutputs['StorageUri'] = $blob.ICloudBlob.StorageUri ", "retentionInterval": "P1D" } } ], "outputs": { "fileUri": { "type": "string", "value": "[reference(variables('deploymentScriptName')).outputs.Uri]" } } }
Save the template.
Open Visual Studio Code, and create a new file called main.bicep.
Copy the following starting template into main.bicep.
var storageAccountName = 'storage${uniqueString(resourceGroup().id)}' var storageBlobContainerName = 'config' var userAssignedIdentityName = 'configDeployer' var roleAssignmentName = guid(resourceGroup().id, 'contributor') var contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') var deploymentScriptName = 'CopyConfigScript' resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: storageAccountName tags: { displayName: storageAccountName } location: resourceGroup().location kind: 'StorageV2' sku: { name: 'Standard_LRS' tier: 'Standard' } properties: { allowBlobPublicAccess: true encryption: { services: { blob: { enabled: true } } keySource: 'Microsoft.Storage' } supportsHttpsTrafficOnly: true } resource blobService 'blobServices' existing = { name: 'default' } } resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = { parent: storageAccount::blobService name: storageBlobContainerName properties: { publicAccess: 'Blob' } } resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { name: userAssignedIdentityName location: resourceGroup().location } resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { name: roleAssignmentName properties: { roleDefinitionId: contributorRoleDefinitionId principalId: userAssignedIdentity.properties.principalId principalType: 'ServicePrincipal' } } resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { name: deploymentScriptName location: resourceGroup().location kind: 'AzurePowerShell' identity: { type: 'UserAssigned' userAssignedIdentities: { '${userAssignedIdentity.id}': {} } } properties: { azPowerShellVersion: '3.0' scriptContent: ''' Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/appsettings.json' -OutFile 'appsettings.json' $storageAccount = Get-AzStorageAccount -ResourceGroupName 'learndeploymentscript_exercise_1' | Where-Object { $_.StorageAccountName -like 'storage*' } $blob = Set-AzStorageBlobContent -File 'appsettings.json' -Container 'config' -Blob 'appsettings.json' -Context $storageAccount.Context $DeploymentScriptOutputs = @{} $DeploymentScriptOutputs['Uri'] = $blob.ICloudBlob.Uri $DeploymentScriptOutputs['StorageUri'] = $blob.ICloudBlob.StorageUri ''' retentionInterval: 'P1D' } dependsOn: [ roleAssignment blobContainer ] } output fileUri string = deploymentScript.properties.outputs.Uri
Save the template.
Update the PowerShell script
Because the other team has done the hard work in creating a PowerShell script to copy multiple files, you decide to use that script in your template.
Edit scriptContent
in the properties
section to include the script that your partner team has provided.
param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
$blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
$DeploymentScriptOutputs[$fileName] = @{}
$DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
$DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
$count++
}
Write-Host \"Finished copying $count files.\"
param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
Write-Host "Copying $fileName to $env:StorageContainerName in $env:StorageAccountName."
Invoke-RestMethod -Uri "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName" -OutFile $fileName
$blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
$DeploymentScriptOutputs[$fileName] = @{}
$DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
$DeploymentScriptOutputs[$fileName]['StorageUri'] = $blob.ICloudBlob.StorageUri
$count++
}
Write-Host "Finished copying $count files."
Add an environment variable
The script you've adopted requires some environment variables. You can specify them directly in the template, but it'll be more flexible to use template functions to get some of the values.
Add an
environmentVariables
property to theproperties
section of the deployment script."environmentVariables": [ ],
Add an environment variable for
ResourceGroupName
."environmentVariables": [ { "name": "ResourceGroupName", "value": "[resourceGroup().name]" } ],
Add an environment variable for
StorageAccountName
."environmentVariables": [ { "name": "ResourceGroupName", "value": "[resourceGroup().name]" }, { "name": "StorageAccountName", "value": "[variables('storageAccountName')]" } ],
Add an environment variable for
StorageContainerName
."environmentVariables": [ { "name": "ResourceGroupName", "value": "[resourceGroup().name]" }, { "name": "StorageAccountName", "value": "[variables('storageAccountName')]" }, { "name": "StorageContainerName", "value": "[variables('storageBlobContainerName')]" } ],
Tip
Use template functions to access common values like [resourceGroup().name]
and [variables()]
.
The script you've adopted requires some environment variables. You can specify them directly in the template, but it'll be more flexible to use Bicep variables to get some of the values.
Add an
environmentVariables
property to theproperties
section of the deployment script.environmentVariables: [ ]
Add an environment variable for
ResourceGroupName
.environmentVariables: [ { name: 'ResourceGroupName' value: resourceGroup().name } ]
Add an environment variable for
StorageAccountName
.environmentVariables: [ { name: 'ResourceGroupName' value: resourceGroup().name } { name: 'StorageAccountName' value: storageAccountName } ]
Add an environment variable for
StorageContainerName
.environmentVariables: [ { name: 'ResourceGroupName' value: resourceGroup().name } { name: 'StorageAccountName' value: storageAccountName } { name: 'StorageContainerName' value: storageBlobContainerName } ]
Add a template parameter
To make your template easier for the two teams to use, you can add a parameter to the template so that each team can specify the files that it wants to copy.
Add a parameter to the template to take an array of filenames.
"parameters": {
"filesToCopy": {
"type": "array",
"metadata": {
"description": "List of files to copy to application storage account."
}
}
},
As a bonus, you can supply a default value so the template will continue to work for your team with no changes to the deployment process. Although not required, entering a new default value can help you understand the pattern of making it easier for teams to adopt new versions of templates if they continue to behave as they've previously done, with the new functionality being the reward. In other words, this step shows you how to maintain the existing behavior while making the changes to support future work.
Add a parameter to the template to take an array of filenames.
@description('List of files to copy to application storage account.')
param filesToCopy array
As a bonus, you can supply a default value so the template will continue to work for your team with no changes to the deployment process. Although not required, entering a new default value can help you understand the pattern of making it easier for teams to adopt new versions of templates if they continue to behave as they've previously done, with the new functionality being the reward. In other words, this step shows you how to maintain the existing behavior while making the changes to support future work.
Add an argument to pass in the files to copy
Next, you can take the parameter that you just defined and pass it in to the deployment script. Passing command-line arguments can be tricky, because the strings are evaluated at multiple levels. Properly escaping quotes and picking the right quotes for the job are essential for success.
Tip
Use template functions to access common functions like [string()]
to convert values of one type to a string.
Add an
arguments
property to the deployment script. The PowerShell script takes a parameter namedFile
, which is a string of filenames that should come from thefilesToCopy
template parameter. Make sure there are quotes around the whole argument so it gets passed in properly.Caution
This
arguments
property is invalid. If you're using the Azure Resource Manager extension in Visual Studio Code, it might flag this line. You'll fix this problem in the next steps."arguments": "[concat( '-File '', string(parameters('filesToCopy')), ''' )]",
Tip
Quoting things in JSON can be hard, especially when you're passing in command-line arguments. You can use a template variable to represent a character that's hard to escape.
Add a template variable to represent the single-quote character.
"variables": { "singleQuote": "'", "storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]", "storageBlobContainerName": "config", "userAssignedIdentityName": "configDeployer", "roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]", "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "deploymentScriptName": "CopyConfigScript" },
Replace the single quotes in the
arguments
property with the variable that you just defined."arguments": "[concat( '-File ', variables('singleQuote'), string(parameters('filesToCopy')), variables('singleQuote'))]",
Next, you can take the parameter that you just defined and pass it in to the deployment script. Passing command-line arguments can be tricky, because the strings are evaluated at multiple levels. Properly escaping quotes and picking the right quotes for the job are essential for success.
Add an arguments
property to the deployment script. The PowerShell script takes a parameter named File
, which is a string of filenames that should come from the filesToCopy
template parameter.
arguments: '-File \'${string(filesToCopy)}\''
Notice this uses several Bicep features:
- String interpolation, to combine the strings.
- We use the
\
escape character to allow us to include a single quote character ('
) inside the string, because a single quote is normally a reserved character in Bicep. - We use the
string()
function to convert thefilesToCopy
array to a string.
Update the template output
Because you're changing the deployment script to deploy one or more files, you need to update the template output to provide all the necessary information.
Update the
outputs
in the template to return the whole object, which will have a URI per file.$DeploymentScriptOutputs = @{} foreach ($fileName in $fileList) { Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\" Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context $DeploymentScriptOutputs[$fileName] = @{} $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri $count++ }
Add another output with the storage account name (which has a random identifier). You'll use this later to validate that the deployment script did what you expected.
$DeploymentScriptOutputs = @{} foreach ($fileName in $fileList) { Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\" Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context $DeploymentScriptOutputs[$fileName] = @{} $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri $count++ }
Update the outputs in the template to return the whole object, which will have a URI per file.
output fileUri object = deploymentScript.properties.outputs
Add another output with the storage account name (which has a random identifier). You'll use this later to validate that the deployment script did what you expected.
output storageAccountName string = storageAccountName
Verify your template
Your template should look similar to:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"apiProfile": "",
"parameters": {
"filesToCopy": {
"type": "array",
"defaultValue": [ "appsettings.json" ],
"metadata": {
"description": "List of files to copy to application storage account."
}
}
},
"variables": {
"singleQuote": "'",
"storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]",
"storageBlobContainerName": "config",
"userAssignedIdentityName": "configDeployer",
"roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]",
"contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"deploymentScriptName": "CopyConfigScript"
},
"functions": [],
"resources": [
{
"name": "[variables('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"tags": {
"displayName": "[variables('storageAccountName')]"
},
"location": "[resourceGroup().location]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"properties": {
"allowBlobPublicAccess": true,
"encryption": {
"services": {
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"supportsHttpsTrafficOnly": true
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2019-04-01",
"name": "[concat(variables('storageAccountName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2019-04-01",
"name": "[concat(variables('storageAccountName'),'/default/',variables('storageBlobContainerName'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"publicAccess": "Blob"
}
},
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"name": "[variables('userAssignedIdentityName')]",
"location": "[resourceGroup().location]"
},
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[variables('roleAssignmentName')]",
"dependsOn": [ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]" ],
"properties": {
"roleDefinitionId": "[variables('contributorRoleDefinitionId')]",
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName')), '2015-08-31-preview').principalId]",
"scope": "[resourceGroup().id]",
"principalType": "ServicePrincipal"
}
},
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "[variables('deploymentScriptName')]",
"location": "[resourceGroup().location]",
"kind": "AzurePowerShell",
"dependsOn": [
"[resourceId('Microsoft.Authorization/roleAssignments', variables('roleAssignmentName'))]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('storageAccountName'), 'default', variables('storageBlobContainerName'))]"
],
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',variables('userAssignedIdentityName'))]": {}
}
},
"properties": {
"arguments": "[concat( '-File ', variables('singleQuote'), string(parameters('filesToCopy')), variables('singleQuote'))]",
"environmentVariables": [
{
"name": "ResourceGroupName",
"value": "[resourceGroup().name]"
},
{
"name": "StorageAccountName",
"value": "[variables('storageAccountName')]"
},
{
"name": "StorageContainerName",
"value": "[variables('storageBlobContainerName')]"
}
],
"azPowerShellVersion": "3.0",
"scriptContent": "
param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
$blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
$DeploymentScriptOutputs[$fileName] = @{}
$DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
$DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
$count++
}
Write-Host \"Finished copying $count files.\"
",
"retentionInterval": "P1D"
}
}
],
"outputs": {
"fileUri": {
"type": "object",
"value": "[reference(variables('deploymentScriptName')).outputs]"
},
"storageAccountName": {
"type": "string",
"value": "[variables('storageAccountName')]"
}
}
}
@description('List of files to copy to application storage account.')
param filesToCopy array = [
'appsettings.json'
]
var storageAccountName = 'storage${uniqueString(resourceGroup().id)}'
var storageBlobContainerName = 'config'
var userAssignedIdentityName = 'configDeployer'
var roleAssignmentName = guid(resourceGroup().id, 'contributor')
var contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
var deploymentScriptName = 'CopyConfigScript'
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
tags: {
displayName: storageAccountName
}
location: resourceGroup().location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
tier: 'Standard'
}
properties: {
allowBlobPublicAccess: true
encryption: {
services: {
blob: {
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
supportsHttpsTrafficOnly: true
}
resource blobService 'blobServices' existing = {
name: 'default'
}
}
resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = {
parent: storageAccount::blobService
name: storageBlobContainerName
properties: {
publicAccess: 'Blob'
}
}
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: userAssignedIdentityName
location: resourceGroup().location
}
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: roleAssignmentName
properties: {
roleDefinitionId: contributorRoleDefinitionId
principalId: userAssignedIdentity.properties.principalId
principalType: 'ServicePrincipal'
}
}
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: deploymentScriptName
location: resourceGroup().location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedIdentity.id}': {}
}
}
properties: {
arguments: '-File \'${string(filesToCopy)}\''
environmentVariables: [
{
name: 'ResourceGroupName'
value: resourceGroup().name
}
{
name: 'StorageAccountName'
value: storageAccountName
}
{
name: 'StorageContainerName'
value: storageBlobContainerName
}
]
azPowerShellVersion: '3.0'
scriptContent: '''
param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
Write-Host "Copying $fileName to $env:StorageContainerName in $env:StorageAccountName."
Invoke-RestMethod -Uri "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName" -OutFile $fileName
$blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
$DeploymentScriptOutputs[$fileName] = @{}
$DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
$DeploymentScriptOutputs[$fileName]['StorageUri'] = $blob.ICloudBlob.StorageUri
$count++
}
Write-Host "Finished copying $count files."
'''
retentionInterval: 'P1D'
}
dependsOn: [
roleAssignment
blobContainer
]
}
output fileUri object = deploymentScript.properties.outputs
output storageAccountName string = storageAccountName
If it doesn't, either copy the example or adjust your template to match the example.
Create a parameters file
Now that you've got the template set, you can validate the new deployment script by using a parameters file with new files specified.
Either create an azuredeploy.parameters.json file manually or use the VS Code extension to do so.
Edit the file to have two
filesToCopy
specified:{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "filesToCopy": { "value": [ "swagger.Staging.json", "appsettings.Staging.json" ] } } }
Create an azuredeploy.parameters.json file.
Edit the file to have two
filesToCopy
specified:{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "filesToCopy": { "value": [ "swagger.Staging.json", "appsettings.Staging.json" ] } } }
Deploy the template
Create a resource group for the exercise
You need to create a resource group to contain the resources that you'll create as part of this exercise. By using a new resource group, you'll make cleaning up after the exercise much easier.
From the terminal in Visual Studio Code, run this command to create the resource group for this exercise.
resourceGroupName="learndeploymentscript_exercise_2"
az group create --location eastus --name $resourceGroupName
$resourceGroupName = 'learndeploymentscript_exercise_2'
New-AzResourceGroup -Location eastus -Name $resourceGroupName
Deploy the template to Azure
Deploy the template by using Azure CLI commands in the Visual Studio Code terminal.
templateFile="azuredeploy.json"
templateParameterFile="azuredeploy.parameters.json"
today=$(date +"%d-%b-%Y")
deploymentName="deploymentscript-"$today
az deployment group create \
--resource-group $resourceGroupName \
--name $deploymentName \
--template-file $templateFile \
--parameters $templateParameterFile
Deploy the template to Azure
Deploy the template by using Azure PowerShell commands in the terminal.
$templateFile = 'azuredeploy.json'
$templateParameterFile = 'azuredeploy.parameters.json'
$today = Get-Date -Format 'MM-dd-yyyy'
$deploymentName = "deploymentscript-$today"
New-AzResourceGroupDeployment `
-ResourceGroupName $resourceGroupName `
-Name $deploymentName `
-TemplateFile $templateFile `
-TemplateParameterFile $templateParameterFile
Deploy the template to Azure
Deploy the template by using Azure CLI commands in the Visual Studio Code terminal.
templateFile="main.bicep"
templateParameterFile="azuredeploy.parameters.json"
today=$(date +"%d-%b-%Y")
deploymentName="deploymentscript-"$today
az deployment group create \
--resource-group $resourceGroupName \
--name $deploymentName \
--template-file $templateFile \
--parameters $templateParameterFile
Deploy the template to Azure
Deploy the template by using Azure PowerShell commands in the terminal.
$templateFile = 'main.bicep'
$templateParameterFile = 'azuredeploy.parameters.json'
$today = Get-Date -Format 'MM-dd-yyyy'
$deploymentName = "deploymentscript-$today"
New-AzResourceGroupDeployment `
-ResourceGroupName $resourceGroupName `
-Name $deploymentName `
-TemplateFile $templateFile `
-TemplateParameterFile $templateParameterFile
Review the result of your template
After the deployment is complete, you can validate that both files were copied to your storage account by listing the contents of the blob container.
List the contents of the blob container.
storageAccountName=$(az deployment group show --resource-group $resourceGroupName --name $deploymentName --query 'properties.outputs.storageAccountName.value' --output tsv) az storage blob list --account-name $storageAccountName --container-name config --query '[].name'
The command returns the following code:
[ "swagger.Staging.json", "appsettings.Staging.json" ]
You can also review the logs (and other details about the deployment) from the Azure portal or by using the following command.
az deployment-scripts show-log --resource-group $resourceGroupName --name CopyConfigScript
List the contents of the blob container.
$storageAccountName = (Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName).Outputs.storageAccountName.Value $storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroupName Get-AzStorageBlob -Context $storageAccount.Context -Container config | Select-Object Name
The command returns the following code:
Name ---- swagger.Staging.json appsettings.Staging.json
You can also review the logs (and other details about the deployment) from the Azure portal or by using the following command.
Get-AzDeploymentScriptLog -ResourceGroupName $resourceGroupName -Name CopyConfigScript
Clean up the resource group
You've successfully deployed an ARM template with a deployment script and used different methods to pass data in to customize its behavior. You can remove the resource group that contains all the resources and role assignments you've created.
az group delete --name $resourceGroupName
Remove-AzResourceGroup -Name $resourceGroupName