From a3cadca9fe625f1cb2085cfae9f4ecdb53fb7d40 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Sat, 7 Sep 2024 18:13:26 +0200 Subject: [PATCH] fix: Image Template Make `customizations` optional - `avm/res/virtual-machine-images/image-template` (#2992) ## Description - The `customizations` property is optional in the API and should also be in the module in case somebody just wants to backup a marketplace image - Fix for script upload (prep for #3005) ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.virtual-machine-images.image-template](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml/badge.svg?branch=users%2Falsehr%2FoptionalCustomization&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .../image-template/README.md | 152 ++++++++---------- .../image-template/main.bicep | 8 +- .../image-template/main.json | 9 +- .../tests/e2e/defaults/main.test.bicep | 6 - .../tests/e2e/max/dependencies.bicep | 4 +- .../tests/e2e/max/main.test.bicep | 2 +- .../Set-StorageContainerContentByEnvVar.ps1 | 6 +- 7 files changed, 84 insertions(+), 103 deletions(-) diff --git a/avm/res/virtual-machine-images/image-template/README.md b/avm/res/virtual-machine-images/image-template/README.md index d8e9e5313d..022c56722c 100644 --- a/avm/res/virtual-machine-images/image-template/README.md +++ b/avm/res/virtual-machine-images/image-template/README.md @@ -45,12 +45,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' - type: 'Shell' - } - { - destination: 'Initialize-LinuxSoftware.ps1' - name: 'Initialize-LinuxSoftware' - sourceUri: '' - type: 'File' - } - { - inline: [ - 'pwsh \'Initialize-LinuxSoftware.ps1\'' - ] - name: 'Software installation' - type: 'Shell' - } - ] distributions: [ { imageName: 'mi-vmiitmax-001' @@ -202,6 +168,26 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' + type: 'Shell' + } + { + destination: 'Initialize-LinuxSoftware.ps1' + name: 'Initialize-LinuxSoftware' + sourceUri: '' + type: 'File' + } + { + inline: [ + 'pwsh \'Initialize-LinuxSoftware.ps1\'' + ] + name: 'Software installation' + type: 'Shell' + } + ] location: '' lock: { kind: 'CanNotDelete' @@ -228,7 +214,7 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' } ] - stagingResourceGroup: '' + stagingResourceGroupResourceId: '' subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -269,28 +255,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:", - "type": "Shell" - }, - { - "destination": "Initialize-LinuxSoftware.ps1", - "name": "Initialize-LinuxSoftware", - "sourceUri": "", - "type": "File" - }, - { - "inline": [ - "pwsh \"Initialize-LinuxSoftware.ps1\"" - ], - "name": "Software installation", - "type": "Shell" - } - ] - }, "distributions": { "value": [ { @@ -334,6 +298,28 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:", + "type": "Shell" + }, + { + "destination": "Initialize-LinuxSoftware.ps1", + "name": "Initialize-LinuxSoftware", + "sourceUri": "", + "type": "File" + }, + { + "inline": [ + "pwsh \"Initialize-LinuxSoftware.ps1\"" + ], + "name": "Software installation", + "type": "Shell" + } + ] + }, "location": { "value": "" }, @@ -370,8 +356,8 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:" + "stagingResourceGroupResourceId": { + "value": "" }, "subnetResourceId": { "value": "" @@ -427,12 +413,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' @@ -453,6 +433,12 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' subnetResourceId: '' tags: { @@ -477,14 +463,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:" }, @@ -539,7 +525,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:If this field is empty, a resource group with a random name will be created.

If the resource group specified in this field doesn't exist, it will be created with the same name.

If the resource group specified exists, it must be empty and in the same region as the image template.

The resource group created will be deleted during template deletion if this field is empty or the resource group specified doesn't exist,

but if the resource group specified exists the resources created in the resource group will be deleted during template deletion and the resource group itself will remain. | +| [`stagingResourceGroupResourceId`](#parameter-stagingresourcegroupresourceid) | string | Resource ID of the staging resource group in the same subscription and location as the image template that will be used to build the image.

If this field is empty, a resource group with a random name will be created.

If the resource group specified in this field doesn't exist, it will be created with the same name.

If the resource group specified exists, it must be empty and in the same region as the image template.

The resource group created will be deleted during template deletion if this field is empty or the resource group specified doesn't exist,

but if the resource group specified exists the resources created in the resource group will be deleted during template deletion and the resource group itself will remain. | | [`subnetResourceId`](#parameter-subnetresourceid) | string | Resource ID of an already existing subnet, e.g.: /subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/.

If no value is provided, a new temporary VNET and subnet will be created in the staging resource group and will be deleted along with the remaining temporary resources. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`validationProcess`](#parameter-validationprocess) | object | Configuration options and list of validations to be performed on the resulting image. | @@ -569,13 +555,6 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:If this field is empty, a resource group with a random name will be created.

If the resource group specified in this field doesn't exist, it will be created with the same name.

If the resource group specified exists, it must be empty and in the same region as the image template.

The resource group created will be deleted during template deletion if this field is empty or the resource group specified doesn't exist,

but if the resource group specified exists the resources created in the resource group will be deleted during template deletion and the resource group itself will remain. diff --git a/avm/res/virtual-machine-images/image-template/main.bicep b/avm/res/virtual-machine-images/image-template/main.bicep index 9aefe03417..c32e5e36f0 100644 --- a/avm/res/virtual-machine-images/image-template/main.bicep +++ b/avm/res/virtual-machine-images/image-template/main.bicep @@ -25,11 +25,11 @@ param subnetResourceId string? @description('Required. Image source definition in object format.') param imageSource object -@description('Required. Customization steps to be run when building the VM image.') -param customizationSteps array +@description('Optional. Customization steps to be run when building the VM image.') +param customizationSteps array? @description('Optional. Resource ID of the staging resource group in the same subscription and location as the image template that will be used to build the image.

If this field is empty, a resource group with a random name will be created.

If the resource group specified in this field doesn\'t exist, it will be created with the same name.

If the resource group specified exists, it must be empty and in the same region as the image template.

The resource group created will be deleted during template deletion if this field is empty or the resource group specified doesn\'t exist,

but if the resource group specified exists the resources created in the resource group will be deleted during template deletion and the resource group itself will remain.') -param stagingResourceGroup string? +param stagingResourceGroupResourceId string? @description('Optional. The lock settings of the service.') param lock lockType @@ -138,7 +138,7 @@ resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2023-07-01 } source: imageSource customize: customizationSteps - stagingResourceGroup: stagingResourceGroup + stagingResourceGroup: stagingResourceGroupResourceId distribute: [ for distribution in distributions: union( { diff --git a/avm/res/virtual-machine-images/image-template/main.json b/avm/res/virtual-machine-images/image-template/main.json index 8af2add54f..df6d5ed0ea 100644 --- a/avm/res/virtual-machine-images/image-template/main.json +++ b/avm/res/virtual-machine-images/image-template/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12715542154180929791" + "templateHash": "495678619291242908" }, "name": "Virtual Machine Image Templates", "description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).", @@ -458,11 +458,12 @@ }, "customizationSteps": { "type": "array", + "nullable": true, "metadata": { - "description": "Required. Customization steps to be run when building the VM image." + "description": "Optional. Customization steps to be run when building the VM image." } }, - "stagingResourceGroup": { + "stagingResourceGroupResourceId": { "type": "string", "nullable": true, "metadata": { @@ -607,7 +608,7 @@ }, "source": "[parameters('imageSource')]", "customize": "[parameters('customizationSteps')]", - "stagingResourceGroup": "[parameters('stagingResourceGroup')]", + "stagingResourceGroup": "[parameters('stagingResourceGroupResourceId')]", "validate": "[parameters('validationProcess')]", "optimize": "[if(not(equals(parameters('optimizeVmBoot'), null())), createObject('vmBoot', createObject('state', parameters('optimizeVmBoot'))), null())]" } diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep index bb557a787c..246c9d8a1f 100644 --- a/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep +++ b/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep @@ -50,12 +50,6 @@ module testDeployment '../../../main.bicep' = { params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - customizationSteps: [ - { - restartTimeout: '30m' - type: 'WindowsRestart' - } - ] imageSource: { offer: 'Windows-11' publisher: 'MicrosoftWindowsDesktop' diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/max/dependencies.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/max/dependencies.bicep index 036e72fb96..0581a62957 100644 --- a/avm/res/virtual-machine-images/image-template/tests/e2e/max/dependencies.bicep +++ b/avm/res/virtual-machine-images/image-template/tests/e2e/max/dependencies.bicep @@ -271,11 +271,11 @@ resource assetsStorageAccount_upload 'Microsoft.Resources/deploymentScripts@2023 scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1') environmentVariables: [ { - name: 'script_Install__LinuxPowerShell_sh' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. + name: '__SCRIPT__Install__LinuxPowerShell_sh' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. value: loadTextContent('src/Install-LinuxPowerShell.sh') } { - name: 'script_Initialize__LinuxSoftware_ps1' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. + name: '__SCRIPT__Initialize__LinuxSoftware_ps1' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. value: loadTextContent('src/Initialize-LinuxSoftware.ps1') } ] diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep index ff20a8a137..f997482ca2 100644 --- a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep +++ b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep @@ -60,7 +60,7 @@ module testDeployment '../../../main.bicep' = { params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - stagingResourceGroup: '${subscription().id}/resourcegroups/${resourceGroupName}-staging' + stagingResourceGroupResourceId: '${subscription().id}/resourcegroups/${resourceGroupName}-staging' customizationSteps: [ { type: 'Shell' diff --git a/avm/utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1 b/avm/utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1 index d0af03f8c2..3d06cd5ab1 100644 --- a/avm/utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1 +++ b/avm/utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1 @@ -15,7 +15,7 @@ Required. The name of the Storage Account to upload to Required. The container to upload the files to .EXAMPLE -Set-StorageContainerContentByEnvVar -StorageAccountName 'mystorage' -TargetContainer 'myContainer' +. 'Set-StorageContainerContentByEnvVar.ps1' -StorageAccountName 'mystorage' -TargetContainer 'myContainer' Upload any required data to the storage account 'mystorage' and container 'myContainer'. #> @@ -33,9 +33,9 @@ Write-Verbose 'Fetching & storing scripts' -Verbose $contentDirectoryName = 'scripts' $contentDirectory = (New-Item $contentDirectoryName -ItemType 'Directory' -Force).FullName $scriptPaths = @() -foreach ($scriptEnvVar in (Get-ChildItem 'env:*').Name | Where-Object { $_ -like 'script_*' }) { +foreach ($scriptEnvVar in (Get-ChildItem 'env:*').Name | Where-Object { $_ -like '__SCRIPT__*' }) { # Handle value like 'script_Initialize__LinuxSoftware_ps1' - $scriptName = $scriptEnvVar -replace 'script_', '' -replace '__', '-' -replace '_', '.' + $scriptName = $scriptEnvVar -replace '__SCRIPT__', '' -replace '__', '-' -replace '_', '.' $scriptContent = (Get-Item env:$scriptEnvVar).Value Write-Verbose ('Storing file [{0}] with length [{1}]' -f $scriptName, $scriptContent.Length) -Verbose