From 13fe2f7ca5124ce014f784bc556d9191f342cad8 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:25:07 +0200 Subject: [PATCH] fix: Various bugfixes for VMSS module (#2540) Fixes: - #1851 Implements: - #2304 "successful run": [![avm.res.compute.virtual-machine-scale-set](https://github.com/rahalan/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml/badge.svg?branch=users%2Frahalan%2FUpdateVMSS)](https://github.com/rahalan/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml) failing run is only failing in my environment, but should work in the BRM environment --------- Co-authored-by: Alexander Sehr --- .../virtual-machine-scale-set/README.md | 540 +++++++++++------- .../virtual-machine-scale-set/main.bicep | 89 ++- .../virtual-machine-scale-set/main.json | 71 ++- .../tests/e2e/windows.max/main.test.bicep | 8 + 4 files changed, 480 insertions(+), 228 deletions(-) diff --git a/avm/res/compute/virtual-machine-scale-set/README.md b/avm/res/compute/virtual-machine-scale-set/README.md index a6bb0e788f..b59cb73447 100644 --- a/avm/res/compute/virtual-machine-scale-set/README.md +++ b/avm/res/compute/virtual-machine-scale-set/README.md @@ -58,18 +58,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslinmin001' - osDisk: { - createOption: 'fromImage' - diskSizeGB: '128' - managedDisk: { - storageAccountType: 'Premium_LRS' - } - } - osType: 'Linux' - skuName: 'Standard_B12ms' - // Non-required parameters - disablePasswordAuthentication: true - location: '' nicConfigurations: [ { ipConfigurations: [ @@ -88,6 +76,18 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s nicSuffix: '-nic01' } ] + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + skuName: 'Standard_B12ms' + // Non-required parameters + disablePasswordAuthentication: true + location: '' publicKeys: [ { keyData: '' @@ -125,6 +125,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslinmin001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslinmin" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -147,26 +167,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "location": { "value": "" }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslinmin" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -204,6 +204,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslinmax001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsslinmax' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -297,24 +315,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsslinmax' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] publicKeys: [ { keyData: '' @@ -369,6 +369,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslinmax001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslinmax" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -496,26 +516,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslinmax" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -584,6 +584,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslcmk001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsslcmk' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -615,24 +633,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s enabled: true } location: '' - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsslcmk' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] publicKeys: [ { keyData: '' @@ -670,6 +670,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslcmk001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslcmk" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -715,26 +735,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "location": { "value": "" }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslcmk" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -772,18 +772,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinmin001' - osDisk: { - createOption: 'fromImage' - diskSizeGB: '128' - managedDisk: { - storageAccountType: 'Premium_LRS' - } - } - osType: 'Windows' - skuName: 'Standard_B12ms' - // Non-required parameters - adminPassword: '' - location: '' nicConfigurations: [ { ipConfigurations: [ @@ -802,6 +790,18 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s nicSuffix: '-nic01' } ] + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + skuName: 'Standard_B12ms' + // Non-required parameters + adminPassword: '' + location: '' } } ``` @@ -833,6 +833,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinmin001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsswinmin" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -854,26 +874,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s }, "location": { "value": "" - }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsswinmin" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] } } } @@ -904,6 +904,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinmax001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsswinmax' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -979,6 +997,14 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s extensionDSCConfig: { enabled: true } + extensionHealthConfig: { + enabled: true + settings: { + port: 80 + protocol: 'http' + requestPath: '/' + } + } extensionMonitoringAgentConfig: { enabled: true } @@ -996,24 +1022,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsswinmax' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] roleAssignments: [ { principalId: '' @@ -1061,6 +1069,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinmax001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsswinmax" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -1158,6 +1186,16 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "enabled": true } }, + "extensionHealthConfig": { + "value": { + "enabled": true, + "settings": { + "port": 80, + "protocol": "http", + "requestPath": "/" + } + } + }, "extensionMonitoringAgentConfig": { "value": { "enabled": true @@ -1185,26 +1223,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsswinmax" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "roleAssignments": { "value": [ { @@ -1262,6 +1280,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinwaf001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsswinwaf' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -1350,24 +1386,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsswinwaf' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] skuCapacity: 1 tags: { Environment: 'Non-Prod' @@ -1408,6 +1426,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinwaf001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsswinwaf" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -1526,26 +1564,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsswinwaf" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "skuCapacity": { "value": 1 }, @@ -1597,6 +1615,7 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`availabilityZones`](#parameter-availabilityzones) | array | The virtual machine scale set zones. NOTE: Availability zones can only be set when you create the scale set. | | [`bootDiagnosticStorageAccountName`](#parameter-bootdiagnosticstorageaccountname) | string | Storage account used to store boot diagnostic information. Boot diagnostics will be disabled if no value is provided. | | [`bootDiagnosticStorageAccountUri`](#parameter-bootdiagnosticstorageaccounturi) | string | Storage account boot diagnostic base URI. | +| [`bypassPlatformSafetyChecksOnUserSchedule`](#parameter-bypassplatformsafetychecksonuserschedule) | bool | Enables customer to schedule patching without accidental upgrades. | | [`customData`](#parameter-customdata) | string | Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. | | [`dataDisks`](#parameter-datadisks) | array | Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | @@ -1613,9 +1632,10 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`extensionAzureDiskEncryptionConfig`](#parameter-extensionazurediskencryptionconfig) | object | The configuration for the [Azure Disk Encryption] extension. Must at least contain the ["enabled": true] property to be executed. Restrictions: Cannot be enabled on disks that have encryption at host enabled. Managed disks encrypted using Azure Disk Encryption cannot be encrypted using customer-managed keys. | | [`extensionCustomScriptConfig`](#parameter-extensioncustomscriptconfig) | object | The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionDependencyAgentConfig`](#parameter-extensiondependencyagentconfig) | object | The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed. | -| [`extensionDomainJoinConfig`](#parameter-extensiondomainjoinconfig) | object | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | +| [`extensionDomainJoinConfig`](#parameter-extensiondomainjoinconfig) | secureObject | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionDomainJoinPassword`](#parameter-extensiondomainjoinpassword) | securestring | Required if name is specified. Password of the user specified in user parameter. | | [`extensionDSCConfig`](#parameter-extensiondscconfig) | object | The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed. | +| [`extensionHealthConfig`](#parameter-extensionhealthconfig) | object | Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionMonitoringAgentConfig`](#parameter-extensionmonitoringagentconfig) | object | The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionNetworkWatcherAgentConfig`](#parameter-extensionnetworkwatcheragentconfig) | object | The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed. | | [`gracePeriod`](#parameter-graceperiod) | string | The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M). | @@ -1631,12 +1651,15 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`monitoringWorkspaceResourceId`](#parameter-monitoringworkspaceresourceid) | string | Resource ID of the monitoring log analytics workspace. | | [`orchestrationMode`](#parameter-orchestrationmode) | string | Specifies the orchestration mode for the virtual machine scale set. | | [`overprovision`](#parameter-overprovision) | bool | Specifies whether the Virtual Machine Scale Set should be overprovisioned. | +| [`patchAssessmentMode`](#parameter-patchassessmentmode) | string | VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. | +| [`patchMode`](#parameter-patchmode) | string | VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'. | | [`pauseTimeBetweenBatches`](#parameter-pausetimebetweenbatches) | string | The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. | | [`plan`](#parameter-plan) | object | Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use. | | [`prioritizeUnhealthyInstances`](#parameter-prioritizeunhealthyinstances) | bool | Upgrade all unhealthy instances in a scale set before any healthy instances. | | [`provisionVMAgent`](#parameter-provisionvmagent) | bool | Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later. | | [`proximityPlacementGroupResourceId`](#parameter-proximityplacementgroupresourceid) | string | Resource ID of a proximity placement group. | | [`publicKeys`](#parameter-publickeys) | array | The list of SSH public keys used to authenticate with linux based VMs. | +| [`rebootSetting`](#parameter-rebootsetting) | string | Specifies the reboot setting for all AutomaticByPlatform patch installation operations. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`rollbackFailedInstancesOnPolicyBreach`](#parameter-rollbackfailedinstancesonpolicybreach) | bool | Rollback failed instances to previous model if the Rolling Upgrade policy is violated. | | [`sasTokenValidityLength`](#parameter-sastokenvaliditylength) | string | SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | @@ -1689,9 +1712,8 @@ Name of the VMSS. Configures NICs and PIPs. -- Required: No +- Required: Yes - Type: array -- Default: `[]` ### Parameter: `osDisk` @@ -1743,7 +1765,7 @@ Specifies whether automatic repairs should be enabled on the virtual machine sca - Required: No - Type: bool -- Default: `False` +- Default: `True` ### Parameter: `availabilityZones` @@ -1776,6 +1798,14 @@ Storage account boot diagnostic base URI. - Type: string - Default: `[format('.blob.{0}/', environment().suffixes.storage)]` +### Parameter: `bypassPlatformSafetyChecksOnUserSchedule` + +Enables customer to schedule patching without accidental upgrades. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `customData` Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. @@ -2031,7 +2061,7 @@ The configuration for the [Dependency Agent] extension. Must at least contain th The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. - Required: No -- Type: object +- Type: secureObject - Default: ```Bicep { @@ -2060,6 +2090,24 @@ The configuration for the [Desired State Configuration] extension. Must at least } ``` +### Parameter: `extensionHealthConfig` + +Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed. + +- Required: No +- Type: object +- Default: + ```Bicep + { + enabled: true + settings: { + port: 80 + protocol: 'http' + requestPath: '/' + } + } + ``` + ### Parameter: `extensionMonitoringAgentConfig` The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. @@ -2252,6 +2300,39 @@ Specifies whether the Virtual Machine Scale Set should be overprovisioned. - Type: bool - Default: `False` +### Parameter: `patchAssessmentMode` + +VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. + +- Required: No +- Type: string +- Default: `'ImageDefault'` +- Allowed: + ```Bicep + [ + 'AutomaticByPlatform' + 'ImageDefault' + ] + ``` + +### Parameter: `patchMode` + +VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'. + +- Required: No +- Type: string +- Default: `'AutomaticByPlatform'` +- Allowed: + ```Bicep + [ + '' + 'AutomaticByOS' + 'AutomaticByPlatform' + 'ImageDefault' + 'Manual' + ] + ``` + ### Parameter: `pauseTimeBetweenBatches` The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. @@ -2300,6 +2381,23 @@ The list of SSH public keys used to authenticate with linux based VMs. - Type: array - Default: `[]` +### Parameter: `rebootSetting` + +Specifies the reboot setting for all AutomaticByPlatform patch installation operations. + +- Required: No +- Type: string +- Default: `'IfRequired'` +- Allowed: + ```Bicep + [ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' + ] + ``` + ### Parameter: `roleAssignments` Array of role assignments to create. diff --git a/avm/res/compute/virtual-machine-scale-set/main.bicep b/avm/res/compute/virtual-machine-scale-set/main.bicep index 40f0918867..f64de8a695 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.bicep +++ b/avm/res/compute/virtual-machine-scale-set/main.bicep @@ -56,7 +56,7 @@ param scaleSetFaultDomain int = 1 param proximityPlacementGroupResourceId string = '' @description('Required. Configures NICs and PIPs.') -param nicConfigurations array = [] +param nicConfigurations array @description('Optional. Specifies the priority for the virtual machine.') @allowed([ @@ -85,6 +85,8 @@ param licenseType string = '' param extensionDomainJoinPassword string = '' @description('Optional. The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed.') +@secure() +#disable-next-line secure-parameter-default param extensionDomainJoinConfig object = { enabled: false } @@ -117,6 +119,16 @@ param extensionAzureDiskEncryptionConfig object = { enabled: false } +@description('Optional. Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionHealthConfig object = { + enabled: true + settings: { + protocol: 'http' + port: 80 + requestPath: '/' + } +} + @description('Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed.') param extensionDSCConfig object = { enabled: false @@ -179,7 +191,7 @@ param enableAutomaticOSUpgrade bool = false param disableAutomaticRollback bool = false @description('Optional. Specifies whether automatic repairs should be enabled on the virtual machine scale set.') -param automaticRepairsPolicyEnabled bool = false +param automaticRepairsPolicyEnabled bool = true @description('Optional. The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M).') param gracePeriod string = 'PT30M' @@ -202,6 +214,35 @@ param provisionVMAgent bool = true @description('Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning.') param enableAutomaticUpdates bool = true +@description('Optional. VM guest patching orchestration mode. \'AutomaticByOS\' & \'Manual\' are for Windows only, \'ImageDefault\' for Linux only. Refer to \'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching\'.') +@allowed([ + 'AutomaticByPlatform' + 'AutomaticByOS' + 'Manual' + 'ImageDefault' + '' +]) +param patchMode string = 'AutomaticByPlatform' + +@description('Optional. Enables customer to schedule patching without accidental upgrades.') +param bypassPlatformSafetyChecksOnUserSchedule bool = true + +@description('Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations.') +@allowed([ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' +]) +param rebootSetting string = 'IfRequired' + +@description('Optional. VM guest patching assessment mode. Set it to \'AutomaticByPlatform\' to enable automatically check for updates every 24 hours.') +@allowed([ + 'AutomaticByPlatform' + 'ImageDefault' +]) +param patchAssessmentMode string = 'ImageDefault' + @description('Optional. Specifies the time zone of the virtual machine. e.g. \'Pacific Standard Time\'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`.') param timeZone string = '' @@ -288,11 +329,35 @@ var linuxConfiguration = { publicKeys: publicKeysFormatted } provisionVMAgent: provisionVMAgent + patchSettings: (provisionVMAgent && (patchMode =~ 'AutomaticByPlatform' || patchMode =~ 'ImageDefault')) + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') + ? { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } + : null + } + : null } var windowsConfiguration = { provisionVMAgent: provisionVMAgent enableAutomaticUpdates: enableAutomaticUpdates + patchSettings: (provisionVMAgent && (patchMode =~ 'AutomaticByPlatform' || patchMode =~ 'AutomaticByOS' || patchMode =~ 'Manual')) + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') + ? { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } + : null + } + : null timeZone: empty(timeZone) ? null : timeZone additionalUnattendContent: empty(additionalUnattendContent) ? null : additionalUnattendContent winRM: !empty(winRM) @@ -534,6 +599,26 @@ resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = { : null } } + extensionProfile: extensionHealthConfig.enabled + ? { + extensions: [ + { + name: 'HealthExtension' + properties: { + publisher: 'Microsoft.ManagedServices' + type: (osType == 'Windows' ? 'ApplicationHealthWindows' : 'ApplicationHealthLinux') + typeHandlerVersion: extensionHealthConfig.?typeHandlerVersion?? '1.0' + autoUpgradeMinorVersion: extensionHealthConfig.?autoUpgradeMinorVersion ?? false + settings: { + protocol: extensionHealthConfig.?protocol ?? 'http' + port: extensionHealthConfig.?port ?? '80' + requestPath: extensionHealthConfig.?requestPath ?? '/' + } + } + } + ] + } + : null licenseType: empty(licenseType) ? null : licenseType priority: vmPriority evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null diff --git a/avm/res/compute/virtual-machine-scale-set/main.json b/avm/res/compute/virtual-machine-scale-set/main.json index 02551b6e2d..d466ab4db7 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.json +++ b/avm/res/compute/virtual-machine-scale-set/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "16376458652852426905" + "templateHash": "10261643860679294572" }, "name": "Virtual Machine Scale Sets", "description": "This module deploys a Virtual Machine Scale Set.", @@ -332,7 +332,6 @@ }, "nicConfigurations": { "type": "array", - "defaultValue": [], "metadata": { "description": "Required. Configures NICs and PIPs." } @@ -383,7 +382,7 @@ } }, "extensionDomainJoinConfig": { - "type": "object", + "type": "secureObject", "defaultValue": { "enabled": false }, @@ -443,6 +442,20 @@ "description": "Optional. The configuration for the [Azure Disk Encryption] extension. Must at least contain the [\"enabled\": true] property to be executed. Restrictions: Cannot be enabled on disks that have encryption at host enabled. Managed disks encrypted using Azure Disk Encryption cannot be encrypted using customer-managed keys." } }, + "extensionHealthConfig": { + "type": "object", + "defaultValue": { + "enabled": true, + "settings": { + "protocol": "http", + "port": 80, + "requestPath": "/" + } + }, + "metadata": { + "description": "Optional. Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, "extensionDSCConfig": { "type": "object", "defaultValue": { @@ -572,7 +585,7 @@ }, "automaticRepairsPolicyEnabled": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Specifies whether automatic repairs should be enabled on the virtual machine scale set." } @@ -618,6 +631,51 @@ "description": "Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning." } }, + "patchMode": { + "type": "string", + "defaultValue": "AutomaticByPlatform", + "allowedValues": [ + "AutomaticByPlatform", + "AutomaticByOS", + "Manual", + "ImageDefault", + "" + ], + "metadata": { + "description": "Optional. VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'." + } + }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enables customer to schedule patching without accidental upgrades." + } + }, + "rebootSetting": { + "type": "string", + "defaultValue": "IfRequired", + "allowedValues": [ + "Always", + "IfRequired", + "Never", + "Unknown" + ], + "metadata": { + "description": "Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations." + } + }, + "patchAssessmentMode": { + "type": "string", + "defaultValue": "ImageDefault", + "allowedValues": [ + "AutomaticByPlatform", + "ImageDefault" + ], + "metadata": { + "description": "Optional. VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours." + } + }, "timeZone": { "type": "string", "defaultValue": "", @@ -791,11 +849,13 @@ "ssh": { "publicKeys": "[variables('publicKeysFormatted')]" }, - "provisionVMAgent": "[parameters('provisionVMAgent')]" + "provisionVMAgent": "[parameters('provisionVMAgent')]", + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('ImageDefault')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]" }, "windowsConfiguration": { "provisionVMAgent": "[parameters('provisionVMAgent')]", "enableAutomaticUpdates": "[parameters('enableAutomaticUpdates')]", + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('AutomaticByOS'))), equals(toLower(parameters('patchMode')), toLower('Manual')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]", "timeZone": "[if(empty(parameters('timeZone')), null(), parameters('timeZone'))]", "additionalUnattendContent": "[if(empty(parameters('additionalUnattendContent')), null(), parameters('additionalUnattendContent'))]", "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), null())]" @@ -949,6 +1009,7 @@ "storageUri": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), format('https://{0}{1}', parameters('bootDiagnosticStorageAccountName'), parameters('bootDiagnosticStorageAccountUri')), null())]" } }, + "extensionProfile": "[if(parameters('extensionHealthConfig').enabled, createObject('extensions', createArray(createObject('name', 'HealthExtension', 'properties', createObject('publisher', 'Microsoft.ManagedServices', 'type', if(equals(parameters('osType'), 'Windows'), 'ApplicationHealthWindows', 'ApplicationHealthLinux'), 'typeHandlerVersion', coalesce(tryGet(parameters('extensionHealthConfig'), 'typeHandlerVersion'), '1.0'), 'autoUpgradeMinorVersion', coalesce(tryGet(parameters('extensionHealthConfig'), 'autoUpgradeMinorVersion'), false()), 'settings', createObject('protocol', coalesce(tryGet(parameters('extensionHealthConfig'), 'protocol'), 'http'), 'port', coalesce(tryGet(parameters('extensionHealthConfig'), 'port'), '80'), 'requestPath', coalesce(tryGet(parameters('extensionHealthConfig'), 'requestPath'), '/')))))), null())]", "licenseType": "[if(empty(parameters('licenseType')), null(), parameters('licenseType'))]", "priority": "[parameters('vmPriority')]", "evictionPolicy": "[if(parameters('enableEvictionPolicy'), 'Deallocate', null())]", diff --git a/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep index ae46d40a0c..80d34f455c 100644 --- a/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep @@ -161,6 +161,14 @@ module testDeployment '../../../main.bicep' = [ extensionNetworkWatcherAgentConfig: { enabled: true } + extensionHealthConfig: { + enabled: true + settings: { + protocol: 'http' + port: 80 + requestPath: '/' + } + } lock: { kind: 'CanNotDelete' name: 'myCustomLockName'