diff --git a/avm/ptn/authorization/policy-assignment/README.md b/avm/ptn/authorization/policy-assignment/README.md index c5fb8cccb4..5d4707af32 100644 --- a/avm/ptn/authorization/policy-assignment/README.md +++ b/avm/ptn/authorization/policy-assignment/README.md @@ -875,7 +875,7 @@ module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { environmentResourceId: '' name: 'acavnet001' // Non-required parameters + additionalPortMappings: [ + { + exposedPort: 8080 + external: false + targetPort: 8080 + } + ] ingressAllowInsecure: false ingressExternal: false ingressTargetPort: 80 @@ -481,6 +488,15 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "value": "acavnet001" }, // Non-required parameters + "additionalPortMappings": { + "value": [ + { + "exposedPort": 8080, + "external": false, + "targetPort": 8080 + } + ] + }, "ingressAllowInsecure": { "value": false }, @@ -667,6 +683,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`activeRevisionsMode`](#parameter-activerevisionsmode) | string | Controls how active revisions are handled for the Container app. | +| [`additionalPortMappings`](#parameter-additionalportmappings) | array | Settings to expose additional ports on container app. | | [`clientCertificateMode`](#parameter-clientcertificatemode) | string | Client certificate mode for mTLS. | | [`corsPolicy`](#parameter-corspolicy) | object | Object userd to configure CORS policy. | | [`customDomains`](#parameter-customdomains) | array | Custom domain bindings for Container App hostnames. | @@ -1070,6 +1087,47 @@ Controls how active revisions are handled for the Container app. ] ``` +### Parameter: `additionalPortMappings` + +Settings to expose additional ports on container app. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`external`](#parameter-additionalportmappingsexternal) | bool | Specifies whether the app port is accessible outside of the environment. | +| [`targetPort`](#parameter-additionalportmappingstargetport) | int | Specifies the port the container listens on. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`exposedPort`](#parameter-additionalportmappingsexposedport) | int | Specifies the exposed port for the target port. If not specified, it defaults to target port. | + +### Parameter: `additionalPortMappings.external` + +Specifies whether the app port is accessible outside of the environment. + +- Required: Yes +- Type: bool + +### Parameter: `additionalPortMappings.targetPort` + +Specifies the port the container listens on. + +- Required: Yes +- Type: int + +### Parameter: `additionalPortMappings.exposedPort` + +Specifies the exposed port for the target port. If not specified, it defaults to target port. + +- Required: No +- Type: int + ### Parameter: `clientCertificateMode` Client certificate mode for mTLS. diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep index 774a5e7bc1..e1a21c434c 100644 --- a/avm/res/app/container-app/main.bicep +++ b/avm/res/app/container-app/main.bicep @@ -47,6 +47,9 @@ param service object = {} @description('Optional. Toggle to include the service configuration.') param includeAddOns bool = false +@description('Optional. Settings to expose additional ports on container app.') +param additionalPortMappings ingressPortMapping[]? + @description('Optional. Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections.') param ingressAllowInsecure bool = true @@ -217,6 +220,7 @@ resource containerApp 'Microsoft.App/containerApps@2024-03-01' = { ingress: disableIngress ? null : { + additionalPortMappings: additionalPortMappings allowInsecure: ingressTransport != 'tcp' ? ingressAllowInsecure : false customDomains: !empty(customDomains) ? customDomains : null corsPolicy: corsPolicy != null && ingressTransport != 'tcp' @@ -387,6 +391,17 @@ type container = { volumeMounts: volumeMount[]? } +type ingressPortMapping = { + @description('Optional. Specifies the exposed port for the target port. If not specified, it defaults to target port.') + exposedPort: int? + + @description('Required. Specifies whether the app port is accessible outside of the environment.') + external: bool + + @description('Required. Specifies the port the container listens on.') + targetPort: int +} + type serviceBind = { @description('Required. The name of the service.') name: string diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json index ced521d34e..be0ad1bf8b 100644 --- a/avm/res/app/container-app/main.json +++ b/avm/res/app/container-app/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16433987478692764186" + "templateHash": "7992196126209120702" }, "name": "Container Apps", "description": "This module deploys a Container App.", @@ -208,6 +208,30 @@ } } }, + "ingressPortMapping": { + "type": "object", + "properties": { + "exposedPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the exposed port for the target port. If not specified, it defaults to target port." + } + }, + "external": { + "type": "bool", + "metadata": { + "description": "Required. Specifies whether the app port is accessible outside of the environment." + } + }, + "targetPort": { + "type": "int", + "metadata": { + "description": "Required. Specifies the port the container listens on." + } + } + } + }, "serviceBind": { "type": "object", "properties": { @@ -584,6 +608,16 @@ "description": "Optional. Toggle to include the service configuration." } }, + "additionalPortMappings": { + "type": "array", + "items": { + "$ref": "#/definitions/ingressPortMapping" + }, + "nullable": true, + "metadata": { + "description": "Optional. Settings to expose additional ports on container app." + } + }, "ingressAllowInsecure": { "type": "bool", "defaultValue": true, @@ -846,7 +880,7 @@ "configuration": { "activeRevisionsMode": "[parameters('activeRevisionsMode')]", "dapr": "[if(not(empty(parameters('dapr'))), parameters('dapr'), null())]", - "ingress": "[if(parameters('disableIngress'), null(), createObject('allowInsecure', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('ingressAllowInsecure'), false()), 'customDomains', if(not(empty(parameters('customDomains'))), parameters('customDomains'), null()), 'corsPolicy', if(and(not(equals(parameters('corsPolicy'), null())), not(equals(parameters('ingressTransport'), 'tcp'))), createObject('allowCredentials', coalesce(tryGet(parameters('corsPolicy'), 'allowCredentials'), false()), 'allowedHeaders', coalesce(tryGet(parameters('corsPolicy'), 'allowedHeaders'), createArray()), 'allowedMethods', coalesce(tryGet(parameters('corsPolicy'), 'allowedMethods'), createArray()), 'allowedOrigins', coalesce(tryGet(parameters('corsPolicy'), 'allowedOrigins'), createArray()), 'exposeHeaders', coalesce(tryGet(parameters('corsPolicy'), 'exposeHeaders'), createArray()), 'maxAge', tryGet(parameters('corsPolicy'), 'maxAge')), null()), 'clientCertificateMode', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('clientCertificateMode'), null()), 'exposedPort', parameters('exposedPort'), 'external', parameters('ingressExternal'), 'ipSecurityRestrictions', if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null()), 'targetPort', parameters('ingressTargetPort'), 'stickySessions', createObject('affinity', parameters('stickySessionsAffinity')), 'traffic', if(not(equals(parameters('ingressTransport'), 'tcp')), createArray(createObject('label', parameters('trafficLabel'), 'latestRevision', parameters('trafficLatestRevision'), 'revisionName', parameters('trafficRevisionName'), 'weight', parameters('trafficWeight'))), null()), 'transport', parameters('ingressTransport')))]", + "ingress": "[if(parameters('disableIngress'), null(), createObject('additionalPortMappings', parameters('additionalPortMappings'), 'allowInsecure', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('ingressAllowInsecure'), false()), 'customDomains', if(not(empty(parameters('customDomains'))), parameters('customDomains'), null()), 'corsPolicy', if(and(not(equals(parameters('corsPolicy'), null())), not(equals(parameters('ingressTransport'), 'tcp'))), createObject('allowCredentials', coalesce(tryGet(parameters('corsPolicy'), 'allowCredentials'), false()), 'allowedHeaders', coalesce(tryGet(parameters('corsPolicy'), 'allowedHeaders'), createArray()), 'allowedMethods', coalesce(tryGet(parameters('corsPolicy'), 'allowedMethods'), createArray()), 'allowedOrigins', coalesce(tryGet(parameters('corsPolicy'), 'allowedOrigins'), createArray()), 'exposeHeaders', coalesce(tryGet(parameters('corsPolicy'), 'exposeHeaders'), createArray()), 'maxAge', tryGet(parameters('corsPolicy'), 'maxAge')), null()), 'clientCertificateMode', if(not(equals(parameters('ingressTransport'), 'tcp')), parameters('clientCertificateMode'), null()), 'exposedPort', parameters('exposedPort'), 'external', parameters('ingressExternal'), 'ipSecurityRestrictions', if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null()), 'targetPort', parameters('ingressTargetPort'), 'stickySessions', createObject('affinity', parameters('stickySessionsAffinity')), 'traffic', if(not(equals(parameters('ingressTransport'), 'tcp')), createArray(createObject('label', parameters('trafficLabel'), 'latestRevision', parameters('trafficLatestRevision'), 'revisionName', parameters('trafficRevisionName'), 'weight', parameters('trafficWeight'))), null()), 'transport', parameters('ingressTransport')))]", "service": "[if(and(parameters('includeAddOns'), not(empty(parameters('service')))), parameters('service'), null())]", "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]", "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]", diff --git a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep index 5a2e4c92e4..3c34893f8c 100644 --- a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep @@ -57,6 +57,14 @@ module testDeployment '../../../main.bicep' = [ ingressTransport: 'tcp' ingressAllowInsecure: false ingressTargetPort: 80 + additionalPortMappings: [ + { + external: false + targetPort: 8080 + exposedPort: 8080 + } + ] + containers: [ { name: 'simple-hello-world-container' diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json index b39a201436..d96f771b50 100644 --- a/avm/res/app/container-app/version.json +++ b/avm/res/app/container-app/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.9", + "version": "0.10", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index 9f4b91ae3d..8e6ccaf128 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -19,10 +19,11 @@ This module deploys a Virtual Machine with one or multiple NICs and optionally o | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Automanage/configurationProfileAssignments` | [2022-05-04](https://learn.microsoft.com/en-us/azure/templates) | -| `Microsoft.Compute/virtualMachines` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-09-01/virtualMachines) | +| `Microsoft.Compute/virtualMachines` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-01/virtualMachines) | | `Microsoft.Compute/virtualMachines/extensions` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines/extensions) | | `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | | `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | +| `Microsoft.Insights/dataCollectionRuleAssociations` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-11/dataCollectionRuleAssociations) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | | `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | @@ -565,8 +566,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true - monitoringWorkspaceResourceId: '' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -892,8 +898,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, - "monitoringWorkspaceResourceId": "", "tags": { "Environment": "Non-Prod", "hidden-title": "This is visible in the resource name", @@ -1196,6 +1207,12 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true tags: { Environment: 'Non-Prod' @@ -1518,6 +1535,12 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, "tags": { "Environment": "Non-Prod", @@ -2332,8 +2355,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true - monitoringWorkspaceResourceId: '' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -2693,8 +2721,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, - "monitoringWorkspaceResourceId": "", "tags": { "Environment": "Non-Prod", "hidden-title": "This is visible in the resource name", @@ -3220,6 +3253,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`disablePasswordAuthentication`](#parameter-disablepasswordauthentication) | bool | Specifies whether password authentication should be disabled. | | [`enableAutomaticUpdates`](#parameter-enableautomaticupdates) | bool | Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. When patchMode is set to Manual, this parameter must be set to false. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning. | | [`enableEvictionPolicy`](#parameter-enableevictionpolicy) | bool | Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy. | +| [`enableHotpatching`](#parameter-enablehotpatching) | bool | Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the 'provisionVMAgent' must be set to true and 'patchMode' must be set to 'AutomaticByPlatform'. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`encryptionAtHost`](#parameter-encryptionathost) | bool | This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | | [`extensionAadJoinConfig`](#parameter-extensionaadjoinconfig) | object | The configuration for the [AAD Join] extension. Must at least contain the ["enabled": true] property to be executed. To enroll in Intune, add the setting mdmId: "0000000a-0000-0000-c000-000000000000". | @@ -3256,7 +3290,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`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. | | [`secureBootEnabled`](#parameter-securebootenabled) | bool | Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | -| [`securityType`](#parameter-securitytype) | string | Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings. | +| [`securityType`](#parameter-securitytype) | string | Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`timeZone`](#parameter-timezone) | string | 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`. | | [`ultraSSDEnabled`](#parameter-ultrassdenabled) | bool | The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled. | @@ -3750,6 +3784,14 @@ Specifies the eviction policy for the low priority virtual machine. Will result - Type: bool - Default: `False` +### Parameter: `enableHotpatching` + +Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the 'provisionVMAgent' must be set to true and 'patchMode' must be set to 'AutomaticByPlatform'. + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. @@ -3912,6 +3954,7 @@ The configuration for the [Monitoring Agent] extension. Must at least contain th - Default: ```Bicep { + dataCollectionRuleAssociations: [] enabled: false } ``` @@ -4277,11 +4320,19 @@ Specifies whether secure boot should be enabled on the virtual machine. This par ### Parameter: `securityType` -Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings. +Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set. - Required: No - Type: string - Default: `''` +- Allowed: + ```Bicep + [ + '' + 'ConfidentialVM' + 'TrustedLaunch' + ] + ``` ### Parameter: `tags` diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index d6ac66d367..0a4f4f476a 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -14,7 +14,12 @@ param vmSize string @description('Optional. This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') param encryptionAtHost bool = true -@description('Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings.') +@description('Optional. Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set.') +@allowed([ + '' + 'ConfidentialVM' + 'TrustedLaunch' +]) param securityType string = '' @description('Optional. Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings.') @@ -159,6 +164,7 @@ param extensionAntiMalwareConfig object = { @description('Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed.') param extensionMonitoringAgentConfig object = { enabled: false + dataCollectionRuleAssociations: [] } @description('Optional. The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed.') @@ -281,6 +287,9 @@ param rebootSetting string = 'IfRequired' ]) param patchAssessmentMode string = 'ImageDefault' +@description('Optional. Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the \'provisionVMAgent\' must be set to true and \'patchMode\' must be set to \'AutomaticByPlatform\'.') +param enableHotpatching bool = false + @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 = '' @@ -327,6 +336,7 @@ var windowsConfiguration = { ? { patchMode: patchMode assessmentMode: patchAssessmentMode + enableHotpatching: (patchMode =~ 'AutomaticByPlatform') ? enableHotpatching : false automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') ? { bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule @@ -493,7 +503,7 @@ module vm_nic 'modules/nic-configuration.bicep' = [ } ] -resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = { +resource vm 'Microsoft.Compute/virtualMachines@2024-03-01' = { name: name location: location identity: identity @@ -749,29 +759,6 @@ module vm_microsoftAntiMalwareExtension 'extension/main.bicep' = if (extensionAn ] } -resource vm_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId)) { - name: last(split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : 'law'), - '/' - ))! - scope: az.resourceGroup( - split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : '//'), - '/' - )[2], - split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : '////'), - '/' - )[4] - ) -} - module vm_azureMonitorAgentExtension 'extension/main.bicep' = if (extensionMonitoringAgentConfig.enabled) { name: '${uniqueString(deployment().name, location)}-VM-AzureMonitorAgent' params: { @@ -783,25 +770,27 @@ module vm_azureMonitorAgentExtension 'extension/main.bicep' = if (extensionMonit typeHandlerVersion: extensionMonitoringAgentConfig.?typeHandlerVersion ?? (osType == 'Windows' ? '1.22' : '1.29') autoUpgradeMinorVersion: extensionMonitoringAgentConfig.?autoUpgradeMinorVersion ?? true enableAutomaticUpgrade: extensionMonitoringAgentConfig.?enableAutomaticUpgrade ?? false - settings: { - workspaceId: !empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? vm_logAnalyticsWorkspace.properties.customerId - : '' - GCS_AUTO_CONFIG: osType == 'Linux' ? true : null - } supressFailures: extensionMonitoringAgentConfig.?supressFailures ?? false tags: extensionMonitoringAgentConfig.?tags ?? tags - protectedSettings: { - workspaceKey: !empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? vm_logAnalyticsWorkspace.listKeys().primarySharedKey - : '' - } } dependsOn: [ vm_microsoftAntiMalwareExtension ] } +resource vm_dataCollectionRuleAssociations 'Microsoft.Insights/dataCollectionRuleAssociations@2023-03-11' = [ + for (dataCollectionRuleAssociation, index) in extensionMonitoringAgentConfig.dataCollectionRuleAssociations: if (extensionMonitoringAgentConfig.enabled) { + name: dataCollectionRuleAssociation.name + scope: vm + properties: { + dataCollectionRuleId: dataCollectionRuleAssociation.dataCollectionRuleResourceId + } + dependsOn: [ + vm_azureMonitorAgentExtension + ] + } +] + module vm_dependencyAgentExtension 'extension/main.bicep' = if (extensionDependencyAgentConfig.enabled) { name: '${uniqueString(deployment().name, location)}-VM-DependencyAgent' params: { diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index 431d7aa3f9..dc46cdf5ed 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17468959759450172802" + "templateHash": "17996854249282289438" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -344,8 +344,13 @@ "securityType": { "type": "string", "defaultValue": "", + "allowedValues": [ + "", + "ConfidentialVM", + "TrustedLaunch" + ], "metadata": { - "description": "Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings." + "description": "Optional. Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set." } }, "secureBootEnabled": { @@ -625,7 +630,8 @@ "extensionMonitoringAgentConfig": { "type": "object", "defaultValue": { - "enabled": false + "enabled": false, + "dataCollectionRuleAssociations": [] }, "metadata": { "description": "Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." @@ -848,6 +854,13 @@ "description": "Optional. VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours." } }, + "enableHotpatching": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the 'provisionVMAgent' must be set to true and 'patchMode' must be set to 'AutomaticByPlatform'." + } + }, "timeZone": { "type": "string", "defaultValue": "", @@ -904,7 +917,7 @@ "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())]", + "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'), 'enableHotpatching', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), parameters('enableHotpatching'), false()), '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())]" @@ -962,7 +975,7 @@ }, "vm": { "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2023-09-01", + "apiVersion": "2024-03-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "identity": "[variables('identity')]", @@ -1110,14 +1123,23 @@ "vm" ] }, - "vm_logAnalyticsWorkspace": { - "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", - "existing": true, - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-06-01", - "subscriptionId": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]]", - "name": "[last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))]" + "vm_dataCollectionRuleAssociations": { + "copy": { + "name": "vm_dataCollectionRuleAssociations", + "count": "[length(parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations)]" + }, + "condition": "[parameters('extensionMonitoringAgentConfig').enabled]", + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2023-03-11", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].name]", + "properties": { + "dataCollectionRuleId": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].dataCollectionRuleResourceId]" + }, + "dependsOn": [ + "vm", + "vm_azureMonitorAgentExtension" + ] }, "AzureWindowsBaseline": { "condition": "[not(empty(parameters('guestConfiguration')))]", @@ -3379,22 +3401,11 @@ "enableAutomaticUpgrade": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'enableAutomaticUpgrade'), false())]" }, - "settings": { - "value": { - "workspaceId": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), reference('vm_logAnalyticsWorkspace').customerId, '')]", - "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" - } - }, "supressFailures": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" }, "tags": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" - }, - "protectedSettings": { - "value": { - "workspaceKey": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2], split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" - } } }, "template": { @@ -3560,7 +3571,6 @@ }, "dependsOn": [ "vm", - "vm_logAnalyticsWorkspace", "vm_microsoftAntiMalwareExtension" ] }, @@ -5380,14 +5390,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('vm', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('vm', '2023-09-01', 'full').location]" + "value": "[reference('vm', '2024-03-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep index 3f7b866feb..b0b2b53a5a 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep @@ -28,12 +28,18 @@ param sshDeploymentScriptName string @description('Required. The name of the SSH Key to create.') param sshKeyName string +@description('Required. The name of the data collection rule.') +param dcrName string + @description('Optional. The location to deploy to.') param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -315,6 +321,93 @@ resource sshKey 'Microsoft.Compute/sshPublicKeys@2022-03-01' = { } } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Linux' + properties: { + dataSources: { + performanceCounters: [ + { + streams: [ + 'Microsoft-Perf' + ] + samplingFrequencyInSeconds: 60 + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + name: 'perfCounterDataSource60' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} + @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -359,3 +452,6 @@ output storageAccountCSEFileName string = storageAccountCSEFileName @description('The Public Key of the created SSH Key.') output SSHKeyPublicKey string = sshKey.properties.publicKey + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep index 04ebe475d7..d52c494855 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep @@ -46,7 +46,9 @@ module nestedDependencies 'dependencies.bicep' = { storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' sshDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -287,12 +289,17 @@ module testDeployment '../../../main.bicep' = { } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' Role: 'DeploymentValidation' } - monitoringWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } extensionNetworkWatcherAgentConfig: { enabled: true diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep index 6f2587a5df..795ab9dfa7 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep @@ -34,6 +34,12 @@ param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. The name of the data collection rule.') +param dcrName string + +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -324,6 +330,93 @@ resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@202 location: location } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Windows' + properties: { + dataSources: { + performanceCounters: [ + { + streams: [ + 'Microsoft-Perf' + ] + samplingFrequencyInSeconds: 60 + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + name: 'perfCounterDataSource60' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} + @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -371,3 +464,6 @@ output storageAccountCSEFileUrl string = '${storageAccount.properties.primaryEnd @description('The resource ID of the created Proximity Placement Group.') output proximityPlacementGroupResourceId string = proximityPlacementGroup.id + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep index bf307d29c7..e12caa5c22 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep @@ -50,7 +50,9 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}01' storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -286,6 +288,12 @@ module testDeployment '../../../main.bicep' = [ } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep index 6f1ef21abd..a492ad2a5a 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep @@ -31,6 +31,12 @@ param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. The name of the data collection rule.') +param dcrName string + +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -288,6 +294,92 @@ resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@202 location: location } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Windows' + properties: { + dataSources: { + performanceCounters: [ + { + streams: [ + 'Microsoft-Perf' + ] + samplingFrequencyInSeconds: 60 + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + name: 'perfCounterDataSource60' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -332,3 +424,6 @@ output storageAccountCSEFileUrl string = '${storageAccount.properties.primaryEnd @description('The resource ID of the created Proximity Placement Group.') output proximityPlacementGroupResourceId string = proximityPlacementGroup.id + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep index 9c4be82965..62a7fd077f 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep @@ -49,7 +49,9 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}01' storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -327,12 +329,17 @@ module testDeployment '../../../main.bicep' = [ } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' Role: 'DeploymentValidation' } - monitoringWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } extensionNetworkWatcherAgentConfig: { enabled: true diff --git a/avm/res/dev-test-lab/lab/README.md b/avm/res/dev-test-lab/lab/README.md index 60f0e50380..5fd0c93362 100644 --- a/avm/res/dev-test-lab/lab/README.md +++ b/avm/res/dev-test-lab/lab/README.md @@ -109,12 +109,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { } artifactsources: [ { - branchRef: 'master' displayName: 'Public Artifact Repo' folderPath: '/Artifacts' name: 'Public Repo' sourceType: 'GitHub' - status: 'Disabled' + status: 'Enabled' uri: 'https://github.com/Azure/azure-devtestlab.git' } { @@ -124,12 +123,28 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { name: 'Public Environment Repo' sourceType: 'GitHub' status: 'Disabled' + tags: { + 'hidden-title': 'This is visible in the resource name' + labName: 'dtllmax001' + resourceType: 'DevTest Lab' + } + uri: 'https://github.com/Azure/azure-devtestlab.git' + } + { + armTemplateFolderPath: '/ArmTemplates' + branchRef: 'main' + displayName: 'Private Artifact Repo' + folderPath: '/Artifacts' + name: 'Private Repo' + securityToken: '' + status: 'Disabled' uri: 'https://github.com/Azure/azure-devtestlab.git' } ] artifactsStorageAccount: '' browserConnect: 'Enabled' costs: { + currencyCode: 'AUD' cycleType: 'CalendarMonth' status: 'Enabled' target: 450 @@ -163,9 +178,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { description: 'Integration configured for auto-shutdown' emailRecipient: 'mail@contosodtlmail.com' events: [ - { - eventName: 'AutoShutdown' - } + 'AutoShutdown' ] name: 'autoShutdown' notificationLocale: 'en' @@ -173,9 +186,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { } { events: [ - { - eventName: 'Cost' - } + 'Cost' ] name: 'costThreshold' webHookUrl: 'https://webhook.contosotest.com' @@ -268,8 +279,10 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { time: '0000' } name: 'LabVmsShutdown' - notificationSettingsStatus: 'Enabled' - notificationSettingsTimeInMinutes: 30 + notificationSettings: { + status: 'Enabled' + timeInMinutes: 30 + } status: 'Enabled' taskType: 'LabVmsShutdownTask' timeZoneId: 'AUS Eastern Standard Time' @@ -367,12 +380,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "artifactsources": { "value": [ { - "branchRef": "master", "displayName": "Public Artifact Repo", "folderPath": "/Artifacts", "name": "Public Repo", "sourceType": "GitHub", - "status": "Disabled", + "status": "Enabled", "uri": "https://github.com/Azure/azure-devtestlab.git" }, { @@ -382,6 +394,21 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "name": "Public Environment Repo", "sourceType": "GitHub", "status": "Disabled", + "tags": { + "hidden-title": "This is visible in the resource name", + "labName": "dtllmax001", + "resourceType": "DevTest Lab" + }, + "uri": "https://github.com/Azure/azure-devtestlab.git" + }, + { + "armTemplateFolderPath": "/ArmTemplates", + "branchRef": "main", + "displayName": "Private Artifact Repo", + "folderPath": "/Artifacts", + "name": "Private Repo", + "securityToken": "", + "status": "Disabled", "uri": "https://github.com/Azure/azure-devtestlab.git" } ] @@ -394,6 +421,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { }, "costs": { "value": { + "currencyCode": "AUD", "cycleType": "CalendarMonth", "status": "Enabled", "target": 450, @@ -451,9 +479,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "description": "Integration configured for auto-shutdown", "emailRecipient": "mail@contosodtlmail.com", "events": [ - { - "eventName": "AutoShutdown" - } + "AutoShutdown" ], "name": "autoShutdown", "notificationLocale": "en", @@ -461,9 +487,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { }, { "events": [ - { - "eventName": "Cost" - } + "Cost" ], "name": "costThreshold", "webHookUrl": "https://webhook.contosotest.com" @@ -564,8 +588,10 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "time": "0000" }, "name": "LabVmsShutdown", - "notificationSettingsStatus": "Enabled", - "notificationSettingsTimeInMinutes": 30, + "notificationSettings": { + "status": "Enabled", + "timeInMinutes": 30 + }, "status": "Enabled", "taskType": "LabVmsShutdownTask", "timeZoneId": "AUS Eastern Standard Time" @@ -741,7 +767,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { | [`labStorageType`](#parameter-labstoragetype) | string | Type of storage used by the lab. It can be either Premium or Standard. | | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | -| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab. | | [`managementIdentitiesResourceIds`](#parameter-managementidentitiesresourceids) | array | The resource ID(s) to assign to the virtual machines associated with this lab. | | [`mandatoryArtifactsResourceIdsLinux`](#parameter-mandatoryartifactsresourceidslinux) | array | The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. | | [`mandatoryArtifactsResourceIdsWindows`](#parameter-mandatoryartifactsresourceidswindows) | array | The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. | @@ -775,7 +801,84 @@ Notification Channels to create for the lab. Required if the schedules property - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`events`](#parameter-notificationchannelsevents) | array | The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost". | +| [`name`](#parameter-notificationchannelsname) | string | The name of the notification channel. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-notificationchannelsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-notificationchannelswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-notificationchannelsdescription) | string | The description of the notification. | +| [`notificationLocale`](#parameter-notificationchannelsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`tags`](#parameter-notificationchannelstags) | object | The tags of the notification channel. | + +### Parameter: `notificationchannels.events` + +The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost". + +- Required: Yes +- Type: array + +### Parameter: `notificationchannels.name` + +The name of the notification channel. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'autoShutdown' + 'costThreshold' + ] + ``` + +### Parameter: `notificationchannels.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.webHookUrl` + +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.description` + +The description of the notification. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.notificationLocale` + +The locale to use when sending a notification (fallback for unsupported languages is EN). + +- Required: No +- Type: string + +### Parameter: `notificationchannels.tags` + +The tags of the notification channel. + +- Required: No +- Type: object ### Parameter: `announcement` @@ -791,227 +894,248 @@ Artifact sources to create for the lab. - Required: No - Type: array -- Default: `[]` -### Parameter: `artifactsStorageAccount` +**Required parameters** -The resource ID of the storage account used to store artifacts and images by the lab. Also used for defaultStorageAccount, defaultPremiumStorageAccount and premiumDataDiskStorageAccount properties. If left empty, a default storage account will be created by the lab and used. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-artifactsourcesname) | string | The name of the artifact source. | +| [`uri`](#parameter-artifactsourcesuri) | string | The artifact source's URI. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`armTemplateFolderPath`](#parameter-artifactsourcesarmtemplatefolderpath) | string | The folder containing Azure Resource Manager templates. Required if "folderPath" is empty. | +| [`folderPath`](#parameter-artifactsourcesfolderpath) | string | The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`branchRef`](#parameter-artifactsourcesbranchref) | string | The artifact source's branch reference (e.g. main or master). | +| [`displayName`](#parameter-artifactsourcesdisplayname) | string | The display name of the artifact source. Default is the name of the artifact source. | +| [`securityToken`](#parameter-artifactsourcessecuritytoken) | securestring | The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity. | +| [`sourceType`](#parameter-artifactsourcessourcetype) | string | The artifact source's type. | +| [`status`](#parameter-artifactsourcesstatus) | string | Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled". | +| [`tags`](#parameter-artifactsourcestags) | object | The tags of the artifact source. | + +### Parameter: `artifactsources.name` + +The name of the artifact source. + +- Required: Yes +- Type: string + +### Parameter: `artifactsources.uri` + +The artifact source's URI. + +- Required: Yes +- Type: string + +### Parameter: `artifactsources.armTemplateFolderPath` + +The folder containing Azure Resource Manager templates. Required if "folderPath" is empty. - Required: No - Type: string -- Default: `''` -### Parameter: `browserConnect` +### Parameter: `artifactsources.folderPath` -Enable browser connect on virtual machines if the lab's VNETs have configured Azure Bastion. +The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty. - Required: No - Type: string -- Default: `'Disabled'` -- Allowed: - ```Bicep - [ - 'Disabled' - 'Enabled' - ] - ``` -### Parameter: `costs` +### Parameter: `artifactsources.branchRef` -Costs to create for the lab. +The artifact source's branch reference (e.g. main or master). - Required: No -- Type: object -- Default: `{}` +- Type: string -### Parameter: `disableAutoUpgradeCseMinorVersion` +### Parameter: `artifactsources.displayName` -Disable auto upgrade custom script extension minor version. +The display name of the artifact source. Default is the name of the artifact source. - Required: No -- Type: bool -- Default: `False` +- Type: string -### Parameter: `enableTelemetry` +### Parameter: `artifactsources.securityToken` -Enable/Disable usage telemetry for module. +The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity. - Required: No -- Type: bool -- Default: `True` +- Type: securestring -### Parameter: `encryptionType` +### Parameter: `artifactsources.sourceType` -Specify how OS and data disks created as part of the lab are encrypted. +The artifact source's type. - Required: No - Type: string -- Default: `'EncryptionAtRestWithPlatformKey'` - Allowed: ```Bicep [ - 'EncryptionAtRestWithCustomerKey' - 'EncryptionAtRestWithPlatformKey' + 'GitHub' + 'StorageAccount' + 'VsoGit' ] ``` -### Parameter: `environmentPermission` +### Parameter: `artifactsources.status` -The access rights to be granted to the user when provisioning an environment. +Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled". - Required: No - Type: string -- Default: `'Reader'` - Allowed: ```Bicep [ - 'Contributor' - 'Reader' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `extendedProperties` +### Parameter: `artifactsources.tags` -Extended properties of the lab used for experimental features. +The tags of the artifact source. - Required: No - Type: object -- Default: `{}` -### Parameter: `isolateLabResources` +### Parameter: `artifactsStorageAccount` -Enable lab resources isolation from the public internet. +The resource ID of the storage account used to store artifacts and images by the lab. Also used for defaultStorageAccount, defaultPremiumStorageAccount and premiumDataDiskStorageAccount properties. If left empty, a default storage account will be created by the lab and used. - Required: No - Type: string -- Default: `'Enabled'` -- Allowed: - ```Bicep - [ - 'Disabled' - 'Enabled' - ] - ``` +- Default: `''` -### Parameter: `labStorageType` +### Parameter: `browserConnect` -Type of storage used by the lab. It can be either Premium or Standard. +Enable browser connect on virtual machines if the lab's VNETs have configured Azure Bastion. - Required: No - Type: string -- Default: `'Premium'` +- Default: `'Disabled'` - Allowed: ```Bicep [ - 'Premium' - 'Standard' - 'StandardSSD' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `location` +### Parameter: `costs` -Location for all Resources. +Costs to create for the lab. - Required: No -- Type: string -- Default: `[resourceGroup().location]` - -### Parameter: `lock` +- Type: object -The lock settings of the service. +**Required parameters** -- Required: No -- Type: object +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cycleType`](#parameter-costscycletype) | string | Reporting cycle type. | -**Optional parameters** +**Conditional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | -| [`name`](#parameter-lockname) | string | Specify the name of lock. | +| [`cycleEndDateTime`](#parameter-costscycleenddatetime) | string | Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". | +| [`cycleStartDateTime`](#parameter-costscyclestartdatetime) | string | Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". | -### Parameter: `lock.kind` +**Optional parameters** -Specify the type of lock. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`currencyCode`](#parameter-costscurrencycode) | string | The currency code of the cost. Default is "USD". | +| [`status`](#parameter-costsstatus) | string | Target cost status. | +| [`tags`](#parameter-coststags) | object | The tags of the resource. | +| [`target`](#parameter-coststarget) | int | Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds. | +| [`thresholdValue100DisplayOnChart`](#parameter-coststhresholdvalue100displayonchart) | string | Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue100SendNotificationWhenExceeded`](#parameter-coststhresholdvalue100sendnotificationwhenexceeded) | string | Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue125DisplayOnChart`](#parameter-coststhresholdvalue125displayonchart) | string | Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue125SendNotificationWhenExceeded`](#parameter-coststhresholdvalue125sendnotificationwhenexceeded) | string | Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue25DisplayOnChart`](#parameter-coststhresholdvalue25displayonchart) | string | Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue25SendNotificationWhenExceeded`](#parameter-coststhresholdvalue25sendnotificationwhenexceeded) | string | Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue50DisplayOnChart`](#parameter-coststhresholdvalue50displayonchart) | string | Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue50SendNotificationWhenExceeded`](#parameter-coststhresholdvalue50sendnotificationwhenexceeded) | string | Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue75DisplayOnChart`](#parameter-coststhresholdvalue75displayonchart) | string | Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue75SendNotificationWhenExceeded`](#parameter-coststhresholdvalue75sendnotificationwhenexceeded) | string | Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | + +### Parameter: `costs.cycleType` + +Reporting cycle type. -- Required: No +- Required: Yes - Type: string - Allowed: ```Bicep [ - 'CanNotDelete' - 'None' - 'ReadOnly' + 'CalendarMonth' + 'Custom' ] ``` -### Parameter: `lock.name` +### Parameter: `costs.cycleEndDateTime` -Specify the name of lock. +Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". - Required: No - Type: string -### Parameter: `managedIdentities` +### Parameter: `costs.cycleStartDateTime` -The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment. +Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". - Required: No -- Type: object - -**Optional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. | - -### Parameter: `managedIdentities.userAssignedResourceIds` - -The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. - -- Required: Yes -- Type: array +- Type: string -### Parameter: `managementIdentitiesResourceIds` +### Parameter: `costs.currencyCode` -The resource ID(s) to assign to the virtual machines associated with this lab. +The currency code of the cost. Default is "USD". - Required: No -- Type: array -- Default: `[]` +- Type: string -### Parameter: `mandatoryArtifactsResourceIdsLinux` +### Parameter: `costs.status` -The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. +Target cost status. - Required: No -- Type: array -- Default: `[]` +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -### Parameter: `mandatoryArtifactsResourceIdsWindows` +### Parameter: `costs.tags` -The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. +The tags of the resource. - Required: No -- Type: array -- Default: `[]` +- Type: object -### Parameter: `policies` +### Parameter: `costs.target` -Policies to create for the lab. +Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds. - Required: No -- Type: array -- Default: `[]` +- Type: int -### Parameter: `premiumDataDisks` +### Parameter: `costs.thresholdValue100DisplayOnChart` -The setting to enable usage of premium data disks. When its value is "Enabled", creation of standard or premium data disks is allowed. When its value is "Disabled", only creation of standard data disks is allowed. Default is "Disabled". +Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts. - Required: No - Type: string -- Default: `'Disabled'` - Allowed: ```Bicep [ @@ -1020,110 +1144,740 @@ The setting to enable usage of premium data disks. When its value is "Enabled", ] ``` -### Parameter: `roleAssignments` +### Parameter: `costs.thresholdValue100SendNotificationWhenExceeded` -Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. - Required: No -- Type: array - +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue125DisplayOnChart` + +Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue125SendNotificationWhenExceeded` + +Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue25DisplayOnChart` + +Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue25SendNotificationWhenExceeded` + +Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue50DisplayOnChart` + +Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue50SendNotificationWhenExceeded` + +Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue75DisplayOnChart` + +Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue75SendNotificationWhenExceeded` + +Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `disableAutoUpgradeCseMinorVersion` + +Disable auto upgrade custom script extension minor version. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `encryptionType` + +Specify how OS and data disks created as part of the lab are encrypted. + +- Required: No +- Type: string +- Default: `'EncryptionAtRestWithPlatformKey'` +- Allowed: + ```Bicep + [ + 'EncryptionAtRestWithCustomerKey' + 'EncryptionAtRestWithPlatformKey' + ] + ``` + +### Parameter: `environmentPermission` + +The access rights to be granted to the user when provisioning an environment. + +- Required: No +- Type: string +- Default: `'Reader'` +- Allowed: + ```Bicep + [ + 'Contributor' + 'Reader' + ] + ``` + +### Parameter: `extendedProperties` + +Extended properties of the lab used for experimental features. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `isolateLabResources` + +Enable lab resources isolation from the public internet. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `labStorageType` + +Type of storage used by the lab. It can be either Premium or Standard. + +- Required: No +- Type: string +- Default: `'Premium'` +- Allowed: + ```Bicep + [ + 'Premium' + 'Standard' + 'StandardSSD' + ] + ``` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. | + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. + +- Required: Yes +- Type: array + +### Parameter: `managementIdentitiesResourceIds` + +The resource ID(s) to assign to the virtual machines associated with this lab. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `mandatoryArtifactsResourceIdsLinux` + +The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `mandatoryArtifactsResourceIdsWindows` + +The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `policies` + +Policies to create for the lab. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`evaluatorType`](#parameter-policiesevaluatortype) | string | The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy). | +| [`factName`](#parameter-policiesfactname) | string | The fact name of the policy. | +| [`name`](#parameter-policiesname) | string | The name of the policy. | +| [`threshold`](#parameter-policiesthreshold) | string | The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy). | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-policiesdescription) | string | The description of the policy. | +| [`factData`](#parameter-policiesfactdata) | string | The fact data of the policy. | +| [`status`](#parameter-policiesstatus) | string | The status of the policy. Default is "Enabled". | + +### Parameter: `policies.evaluatorType` + +The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy). + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AllowedValuesPolicy' + 'MaxValuePolicy' + ] + ``` + +### Parameter: `policies.factName` + +The fact name of the policy. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'EnvironmentTemplate' + 'GalleryImage' + 'LabPremiumVmCount' + 'LabTargetCost' + 'LabVmCount' + 'LabVmSize' + 'ScheduleEditPermission' + 'UserOwnedLabPremiumVmCount' + 'UserOwnedLabVmCount' + 'UserOwnedLabVmCountInSubnet' + ] + ``` + +### Parameter: `policies.name` + +The name of the policy. + +- Required: Yes +- Type: string + +### Parameter: `policies.threshold` + +The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy). + +- Required: Yes +- Type: string + +### Parameter: `policies.description` + +The description of the policy. + +- Required: No +- Type: string + +### Parameter: `policies.factData` + +The fact data of the policy. + +- Required: No +- Type: string + +### Parameter: `policies.status` + +The status of the policy. Default is "Enabled". + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `premiumDataDisks` + +The setting to enable usage of premium data disks. When its value is "Enabled", creation of standard or premium data disks is allowed. When its value is "Disabled", only creation of standard data disks is allowed. Default is "Disabled". + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `roleAssignments` + +Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `schedules` + +Schedules to create for the lab. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-schedulesname) | string | The name of the schedule. | +| [`taskType`](#parameter-schedulestasktype) | string | The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask). | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dailyRecurrence`](#parameter-schedulesdailyrecurrence) | object | The daily recurrence of the schedule. | +| [`hourlyRecurrence`](#parameter-scheduleshourlyrecurrence) | object | If the schedule will occur multiple times a day, specify the hourly recurrence. | +| [`notificationSettings`](#parameter-schedulesnotificationsettings) | object | The notification settings for the schedule. | +| [`status`](#parameter-schedulesstatus) | string | The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled". | +| [`tags`](#parameter-schedulestags) | object | The tags of the schedule. | +| [`targetResourceId`](#parameter-schedulestargetresourceid) | string | The resource ID to which the schedule belongs. | +| [`timeZoneId`](#parameter-schedulestimezoneid) | string | The time zone ID of the schedule. Defaults to "Pacific Standard time". | +| [`weeklyRecurrence`](#parameter-schedulesweeklyrecurrence) | object | If the schedule will occur only some days of the week, specify the weekly recurrence. | + +### Parameter: `schedules.name` + +The name of the schedule. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'LabVmAutoStart' + 'LabVmsShutdown' + ] + ``` + +### Parameter: `schedules.taskType` + +The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask). + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'LabVmsShutdownTask' + 'LabVmsStartupTask' + ] + ``` + +### Parameter: `schedules.dailyRecurrence` + +The daily recurrence of the schedule. + +- Required: No +- Type: object + **Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | -| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| [`time`](#parameter-schedulesdailyrecurrencetime) | string | The time of day the schedule will occur. | -**Optional parameters** +### Parameter: `schedules.dailyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `schedules.hourlyRecurrence` + +If the schedule will occur multiple times a day, specify the hourly recurrence. + +- Required: No +- Type: object + +**Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | -| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | -| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | -| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | -| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | -| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | +| [`minute`](#parameter-scheduleshourlyrecurrenceminute) | int | Minutes of the hour the schedule will run. | -### Parameter: `roleAssignments.principalId` +### Parameter: `schedules.hourlyRecurrence.minute` -The principal ID of the principal (user/group/identity) to assign the role to. +Minutes of the hour the schedule will run. - Required: Yes +- Type: int + +### Parameter: `schedules.notificationSettings` + +The notification settings for the schedule. + +- Required: No +- Type: object + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-schedulesnotificationsettingsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-schedulesnotificationsettingswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`notificationLocale`](#parameter-schedulesnotificationsettingsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`status`](#parameter-schedulesnotificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. | +| [`timeInMinutes`](#parameter-schedulesnotificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. | + +### Parameter: `schedules.notificationSettings.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No - Type: string -### Parameter: `roleAssignments.roleDefinitionIdOrName` +### Parameter: `schedules.notificationSettings.webHookUrl` -The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. -- Required: Yes +- Required: No - Type: string -### Parameter: `roleAssignments.condition` +### Parameter: `schedules.notificationSettings.notificationLocale` -The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". +The locale to use when sending a notification (fallback for unsupported languages is EN). - Required: No - Type: string -### Parameter: `roleAssignments.conditionVersion` +### Parameter: `schedules.notificationSettings.status` -Version of the condition. +If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. - Required: No - Type: string - Allowed: ```Bicep [ - '2.0' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` +### Parameter: `schedules.notificationSettings.timeInMinutes` -The Resource Id of the delegated managed identity resource. +Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. - Required: No -- Type: string +- Type: int -### Parameter: `roleAssignments.description` +### Parameter: `schedules.status` -The description of the role assignment. +The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled". - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -### Parameter: `roleAssignments.name` +### Parameter: `schedules.tags` -The name (as GUID) of the role assignment. If not provided, a GUID will be generated. +The tags of the schedule. + +- Required: No +- Type: object + +### Parameter: `schedules.targetResourceId` + +The resource ID to which the schedule belongs. - Required: No - Type: string -### Parameter: `roleAssignments.principalType` +### Parameter: `schedules.timeZoneId` -The principal type of the assigned principal ID. +The time zone ID of the schedule. Defaults to "Pacific Standard time". - Required: No - Type: string -- Allowed: - ```Bicep - [ - 'Device' - 'ForeignGroup' - 'Group' - 'ServicePrincipal' - 'User' - ] - ``` -### Parameter: `schedules` +### Parameter: `schedules.weeklyRecurrence` -Schedules to create for the lab. +If the schedule will occur only some days of the week, specify the weekly recurrence. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-schedulesweeklyrecurrencetime) | string | The time of day the schedule will occur. | +| [`weekdays`](#parameter-schedulesweeklyrecurrenceweekdays) | array | The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). | + +### Parameter: `schedules.weeklyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `schedules.weeklyRecurrence.weekdays` + +The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). + +- Required: Yes - Type: array -- Default: `[]` ### Parameter: `support` @@ -1146,7 +1900,221 @@ Virtual networks to create for the lab. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`externalProviderResourceId`](#parameter-virtualnetworksexternalproviderresourceid) | string | The external provider resource ID of the virtual network. | +| [`name`](#parameter-virtualnetworksname) | string | The name of the virtual network. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedSubnets`](#parameter-virtualnetworksallowedsubnets) | array | The allowed subnets of the virtual network. | +| [`description`](#parameter-virtualnetworksdescription) | string | The description of the virtual network. | +| [`subnetOverrides`](#parameter-virtualnetworkssubnetoverrides) | array | The subnet overrides of the virtual network. | +| [`tags`](#parameter-virtualnetworkstags) | object | The tags of the virtual network. | + +### Parameter: `virtualnetworks.externalProviderResourceId` + +The external provider resource ID of the virtual network. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.name` + +The name of the virtual network. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets` + +The allowed subnets of the virtual network. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-virtualnetworksallowedsubnetslabsubnetname) | string | The name of the subnet as seen in the lab. | +| [`resourceId`](#parameter-virtualnetworksallowedsubnetsresourceid) | string | The resource ID of the allowed subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowPublicIp`](#parameter-virtualnetworksallowedsubnetsallowpublicip) | string | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | + +### Parameter: `virtualnetworks.allowedSubnets.labSubnetName` + +The name of the subnet as seen in the lab. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets.resourceId` + +The resource ID of the allowed subnet. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets.allowPublicIp` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.description` + +The description of the virtual network. + +- Required: No +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides` + +The subnet overrides of the virtual network. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-virtualnetworkssubnetoverrideslabsubnetname) | string | The name given to the subnet within the lab. | +| [`resourceId`](#parameter-virtualnetworkssubnetoverridesresourceid) | string | The resource ID of the subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`sharedPublicIpAddressConfiguration`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfiguration) | object | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | +| [`useInVmCreationPermission`](#parameter-virtualnetworkssubnetoverridesuseinvmcreationpermission) | string | Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). | +| [`usePublicIpAddressPermission`](#parameter-virtualnetworkssubnetoverridesusepublicipaddresspermission) | string | Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). | +| [`virtualNetworkPoolName`](#parameter-virtualnetworkssubnetoverridesvirtualnetworkpoolname) | string | The virtual network pool associated with this subnet. | + +### Parameter: `virtualnetworks.subnetOverrides.labSubnetName` + +The name given to the subnet within the lab. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides.resourceId` + +The resource ID of the subnet. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedPorts`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedports) | array | Backend ports that virtual machines on this subnet are allowed to expose. | + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts` + +Backend ports that virtual machines on this subnet are allowed to expose. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`backendPort`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedportsbackendport) | int | Backend port of the target virtual machine. | +| [`transportProtocol`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedportstransportprotocol) | string | Protocol type of the port. | + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.backendPort` + +Backend port of the target virtual machine. + +- Required: Yes +- Type: int + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.transportProtocol` + +Protocol type of the port. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Tcp' + 'Udp' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.useInVmCreationPermission` + +Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.usePublicIpAddressPermission` + +Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.virtualNetworkPoolName` + +The virtual network pool associated with this subnet. + +- Required: No +- Type: string + +### Parameter: `virtualnetworks.tags` + +The tags of the virtual network. + +- Required: No +- Type: object ### Parameter: `vmCreationResourceGroupId` diff --git a/avm/res/dev-test-lab/lab/artifactsource/README.md b/avm/res/dev-test-lab/lab/artifactsource/README.md index 317b5d0302..4499ea5096 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/README.md +++ b/avm/res/dev-test-lab/lab/artifactsource/README.md @@ -66,7 +66,6 @@ The folder containing Azure Resource Manager templates. Required if "folderPath" - Required: No - Type: string -- Default: `''` ### Parameter: `folderPath` @@ -74,7 +73,6 @@ The folder containing artifacts. At least one folder path is required. Required - Required: No - Type: string -- Default: `''` ### Parameter: `labName` @@ -89,7 +87,6 @@ The artifact source's branch reference (e.g. main or master). - Required: No - Type: string -- Default: `''` ### Parameter: `displayName` @@ -105,7 +102,6 @@ The security token to authenticate to the artifact source. - Required: No - Type: securestring -- Default: `''` ### Parameter: `sourceType` @@ -113,11 +109,9 @@ The artifact source's type. - Required: No - Type: string -- Default: `''` - Allowed: ```Bicep [ - '' 'GitHub' 'StorageAccount' 'VsoGit' diff --git a/avm/res/dev-test-lab/lab/artifactsource/main.bicep b/avm/res/dev-test-lab/lab/artifactsource/main.bicep index b350884783..cf9fecf54b 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/main.bicep +++ b/avm/res/dev-test-lab/lab/artifactsource/main.bicep @@ -17,26 +17,25 @@ param tags object? param displayName string = name @sys.description('Optional. The artifact source\'s branch reference (e.g. main or master).') -param branchRef string = '' +param branchRef string? @sys.description('Conditional. The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty.') -param folderPath string = '' +param folderPath string? @sys.description('Conditional. The folder containing Azure Resource Manager templates. Required if "folderPath" is empty.') -param armTemplateFolderPath string = '' +param armTemplateFolderPath string? @sys.description('Optional. The security token to authenticate to the artifact source.') @secure() -param securityToken string = '' +param securityToken string? @allowed([ - '' 'GitHub' 'StorageAccount' 'VsoGit' ]) @sys.description('Optional. The artifact source\'s type.') -param sourceType string = '' +param sourceType string? @allowed([ 'Enabled' @@ -58,11 +57,11 @@ resource artifactsource 'Microsoft.DevTestLab/labs/artifactsources@2018-09-15' = tags: tags properties: { displayName: displayName - branchRef: !empty(branchRef) ? branchRef : null - folderPath: !empty(folderPath) ? folderPath : null - armTemplateFolderPath: !empty(armTemplateFolderPath) ? armTemplateFolderPath : null - securityToken: !empty(securityToken) ? securityToken : null - sourceType: !empty(sourceType) ? sourceType : null + branchRef: branchRef + folderPath: folderPath + armTemplateFolderPath: armTemplateFolderPath + securityToken: securityToken + sourceType: sourceType status: status uri: uri } diff --git a/avm/res/dev-test-lab/lab/artifactsource/main.json b/avm/res/dev-test-lab/lab/artifactsource/main.json index cf2720c644..d6c775b85b 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/main.json +++ b/avm/res/dev-test-lab/lab/artifactsource/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15552685312013632487" + "templateHash": "13904061272597362111" }, "name": "DevTest Lab Artifact Sources", - "description": "This module deploys a DevTest Lab Artifact Source.\n\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", + "description": "This module deploys a DevTest Lab Artifact Source.\r\n\r\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -41,37 +41,36 @@ }, "branchRef": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The artifact source's branch reference (e.g. main or master)." } }, "folderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." } }, "armTemplateFolderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." } }, "securityToken": { "type": "securestring", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The security token to authenticate to the artifact source." } }, "sourceType": { "type": "string", - "defaultValue": "", + "nullable": true, "allowedValues": [ - "", "GitHub", "StorageAccount", "VsoGit" @@ -112,11 +111,11 @@ "tags": "[parameters('tags')]", "properties": { "displayName": "[parameters('displayName')]", - "branchRef": "[if(not(empty(parameters('branchRef'))), parameters('branchRef'), null())]", - "folderPath": "[if(not(empty(parameters('folderPath'))), parameters('folderPath'), null())]", - "armTemplateFolderPath": "[if(not(empty(parameters('armTemplateFolderPath'))), parameters('armTemplateFolderPath'), null())]", - "securityToken": "[if(not(empty(parameters('securityToken'))), parameters('securityToken'), null())]", - "sourceType": "[if(not(empty(parameters('sourceType'))), parameters('sourceType'), null())]", + "branchRef": "[parameters('branchRef')]", + "folderPath": "[parameters('folderPath')]", + "armTemplateFolderPath": "[parameters('armTemplateFolderPath')]", + "securityToken": "[parameters('securityToken')]", + "sourceType": "[parameters('sourceType')]", "status": "[parameters('status')]", "uri": "[parameters('uri')]" }, diff --git a/avm/res/dev-test-lab/lab/cost/README.md b/avm/res/dev-test-lab/lab/cost/README.md index 084c7e069d..861224b60f 100644 --- a/avm/res/dev-test-lab/lab/cost/README.md +++ b/avm/res/dev-test-lab/lab/cost/README.md @@ -73,7 +73,6 @@ Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z) - Required: No - Type: string -- Default: `''` ### Parameter: `cycleStartDateTime` @@ -81,7 +80,6 @@ Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000 - Required: No - Type: string -- Default: `''` ### Parameter: `labName` diff --git a/avm/res/dev-test-lab/lab/cost/main.bicep b/avm/res/dev-test-lab/lab/cost/main.bicep index a380c409b0..0cd9280b88 100644 --- a/avm/res/dev-test-lab/lab/cost/main.bicep +++ b/avm/res/dev-test-lab/lab/cost/main.bicep @@ -18,10 +18,10 @@ param cycleType string param tags object? @sys.description('Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') -param cycleStartDateTime string = '' +param cycleStartDateTime string? @sys.description('Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') -param cycleEndDateTime string = '' +param cycleEndDateTime string? @allowed([ 'Enabled' diff --git a/avm/res/dev-test-lab/lab/cost/main.json b/avm/res/dev-test-lab/lab/cost/main.json index 3b38827bc4..ff19b73a67 100644 --- a/avm/res/dev-test-lab/lab/cost/main.json +++ b/avm/res/dev-test-lab/lab/cost/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16348143074487982445" + "templateHash": "7509251296299887127" }, "name": "DevTest Lab Costs", - "description": "This module deploys a DevTest Lab Cost.\n\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", + "description": "This module deploys a DevTest Lab Cost.\r\n\r\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -38,14 +38,14 @@ }, "cycleStartDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } }, "cycleEndDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } diff --git a/avm/res/dev-test-lab/lab/main.bicep b/avm/res/dev-test-lab/lab/main.bicep index 15db191482..3d0fe3a7ce 100644 --- a/avm/res/dev-test-lab/lab/main.bicep +++ b/avm/res/dev-test-lab/lab/main.bicep @@ -57,7 +57,7 @@ param premiumDataDisks string = 'Disabled' @description('Optional. The properties of any lab support message associated with this lab.') param support object = {} -@description('Optional. The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment.') +@description('Optional. The managed identity definition for this resource. For new labs created after 8/10/2020, the lab\'s system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab.') param managedIdentities managedIdentitiesType @description('Optional. The resource ID(s) to assign to the virtual machines associated with this lab.') @@ -94,22 +94,22 @@ param encryptionType string = 'EncryptionAtRestWithPlatformKey' param encryptionDiskEncryptionSetId string = '' @description('Optional. Virtual networks to create for the lab.') -param virtualnetworks array = [] +param virtualnetworks virtualNetworkType @description('Optional. Policies to create for the lab.') -param policies array = [] +param policies policiesType @description('Optional. Schedules to create for the lab.') -param schedules array = [] +param schedules scheduleType @description('Conditional. Notification Channels to create for the lab. Required if the schedules property "notificationSettingsStatus" is set to "Enabled.') -param notificationchannels array = [] +param notificationchannels notificationChannelType @description('Optional. Artifact sources to create for the lab.') -param artifactsources array = [] +param artifactsources artifactsourcesType @description('Optional. Costs to create for the lab.') -param costs object = {} +param costs costsType @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true @@ -125,7 +125,9 @@ var identity = !empty(managedIdentities) type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned' userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } - : any(null) + : { + type: 'SystemAssigned' + } var formattedManagementIdentities = !empty(managementIdentitiesResourceIds) ? reduce(map((managementIdentitiesResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) @@ -226,95 +228,86 @@ resource lab_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ? } module lab_virtualNetworks 'virtualnetwork/main.bicep' = [ - for (virtualNetwork, index) in virtualnetworks: { + for (virtualNetwork, index) in (virtualnetworks ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-VirtualNetwork-${index}' params: { labName: lab.name name: virtualNetwork.name tags: virtualNetwork.?tags ?? tags externalProviderResourceId: virtualNetwork.externalProviderResourceId - description: contains(virtualNetwork, 'description') ? virtualNetwork.description : '' - allowedSubnets: contains(virtualNetwork, 'allowedSubnets') ? virtualNetwork.allowedSubnets : [] - subnetOverrides: contains(virtualNetwork, 'subnetOverrides') ? virtualNetwork.subnetOverrides : [] + description: virtualNetwork.?description + allowedSubnets: virtualNetwork.?allowedSubnets + subnetOverrides: virtualNetwork.?subnetOverrides } } ] module lab_policies 'policyset/policy/main.bicep' = [ - for (policy, index) in policies: { + for (policy, index) in (policies ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-PolicySets-Policy-${index}' params: { labName: lab.name name: policy.name - - description: contains(policy, 'description') ? policy.description : '' + description: policy.?description evaluatorType: policy.evaluatorType - factData: contains(policy, 'factData') ? policy.factData : '' + factData: policy.?factData factName: policy.factName - status: contains(policy, 'status') ? policy.status : 'Enabled' + status: policy.?status ?? 'Enabled' threshold: policy.threshold } } ] module lab_schedules 'schedule/main.bicep' = [ - for (schedule, index) in schedules: { + for (schedule, index) in (schedules ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-Schedules-${index}' params: { labName: lab.name name: schedule.name tags: schedule.?tags ?? tags taskType: schedule.taskType - dailyRecurrence: contains(schedule, 'dailyRecurrence') ? schedule.dailyRecurrence : {} - hourlyRecurrence: contains(schedule, 'hourlyRecurrence') ? schedule.hourlyRecurrence : {} - weeklyRecurrence: contains(schedule, 'weeklyRecurrence') ? schedule.weeklyRecurrence : {} - status: contains(schedule, 'status') ? schedule.status : 'Enabled' - targetResourceId: contains(schedule, 'targetResourceId') ? schedule.targetResourceId : '' - timeZoneId: contains(schedule, 'timeZoneId') ? schedule.timeZoneId : 'Pacific Standard time' - notificationSettingsStatus: contains(schedule, 'notificationSettingsStatus') - ? schedule.notificationSettingsStatus - : 'Disabled' - notificationSettingsTimeInMinutes: contains(schedule, 'notificationSettingsTimeInMinutes') - ? schedule.notificationSettingsTimeInMinutes - : 30 + dailyRecurrence: schedule.?dailyRecurrence + hourlyRecurrence: schedule.?hourlyRecurrence + weeklyRecurrence: schedule.?weeklyRecurrence + status: schedule.?status ?? 'Enabled' + targetResourceId: schedule.?targetResourceId + timeZoneId: schedule.?timeZoneId ?? 'Pacific Standard time' + notificationSettings: schedule.?notificationSettings } } ] module lab_notificationChannels 'notificationchannel/main.bicep' = [ - for (notificationChannel, index) in notificationchannels: { + for (notificationChannel, index) in (notificationchannels ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-NotificationChannels-${index}' params: { labName: lab.name name: notificationChannel.name tags: notificationChannel.?tags ?? tags - description: contains(notificationChannel, 'description') ? notificationChannel.description : '' + description: notificationChannel.?description events: notificationChannel.events - emailRecipient: contains(notificationChannel, 'emailRecipient') ? notificationChannel.emailRecipient : '' - webHookUrl: contains(notificationChannel, 'webhookUrl') ? notificationChannel.webhookUrl : '' - notificationLocale: contains(notificationChannel, 'notificationLocale') - ? notificationChannel.notificationLocale - : 'en' + emailRecipient: notificationChannel.?emailRecipient + webHookUrl: notificationChannel.?webHookUrl + notificationLocale: notificationChannel.?notificationLocale ?? 'en' } } ] module lab_artifactSources 'artifactsource/main.bicep' = [ - for (artifactSource, index) in artifactsources: { + for (artifactSource, index) in (artifactsources ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-ArtifactSources-${index}' params: { labName: lab.name name: artifactSource.name tags: artifactSource.?tags ?? tags - displayName: contains(artifactSource, 'displayName') ? artifactSource.displayName : artifactSource.name - branchRef: contains(artifactSource, 'branchRef') ? artifactSource.branchRef : '' - folderPath: contains(artifactSource, 'folderPath') ? artifactSource.folderPath : '' - armTemplateFolderPath: contains(artifactSource, 'armTemplateFolderPath') - ? artifactSource.armTemplateFolderPath - : '' - sourceType: contains(artifactSource, 'sourceType') ? artifactSource.sourceType : '' - status: contains(artifactSource, 'status') ? artifactSource.status : 'Enabled' + displayName: artifactSource.?displayName ?? artifactSource.name + branchRef: artifactSource.?branchRef + folderPath: artifactSource.?folderPath + armTemplateFolderPath: artifactSource.?armTemplateFolderPath + sourceType: artifactSource.?sourceType + status: artifactSource.?status ?? 'Enabled' uri: artifactSource.uri + securityToken: artifactSource.?securityToken } } ] @@ -324,42 +317,22 @@ module lab_costs 'cost/main.bicep' = if (!empty(costs)) { params: { labName: lab.name tags: costs.?tags ?? tags - currencyCode: contains(costs, 'currencyCode') ? costs.currencyCode : 'USD' - cycleType: costs.cycleType - cycleStartDateTime: contains(costs, 'cycleStartDateTime') ? costs.cycleStartDateTime : '' - cycleEndDateTime: contains(costs, 'cycleEndDateTime') ? costs.cycleEndDateTime : '' - status: contains(costs, 'status') ? costs.status : 'Enabled' - target: contains(costs, 'target') ? costs.target : 0 - thresholdValue25DisplayOnChart: contains(costs, 'thresholdValue25DisplayOnChart') - ? costs.thresholdValue25DisplayOnChart - : 'Disabled' - thresholdValue25SendNotificationWhenExceeded: contains(costs, 'thresholdValue25SendNotificationWhenExceeded') - ? costs.thresholdValue25SendNotificationWhenExceeded - : 'Disabled' - thresholdValue50DisplayOnChart: contains(costs, 'thresholdValue50DisplayOnChart') - ? costs.thresholdValue50DisplayOnChart - : 'Disabled' - thresholdValue50SendNotificationWhenExceeded: contains(costs, 'thresholdValue50SendNotificationWhenExceeded') - ? costs.thresholdValue50SendNotificationWhenExceeded - : 'Disabled' - thresholdValue75DisplayOnChart: contains(costs, 'thresholdValue75DisplayOnChart') - ? costs.thresholdValue75DisplayOnChart - : 'Disabled' - thresholdValue75SendNotificationWhenExceeded: contains(costs, 'thresholdValue75SendNotificationWhenExceeded') - ? costs.thresholdValue75SendNotificationWhenExceeded - : 'Disabled' - thresholdValue100DisplayOnChart: contains(costs, 'thresholdValue100DisplayOnChart') - ? costs.thresholdValue100DisplayOnChart - : 'Disabled' - thresholdValue100SendNotificationWhenExceeded: contains(costs, 'thresholdValue100SendNotificationWhenExceeded') - ? costs.thresholdValue100SendNotificationWhenExceeded - : 'Disabled' - thresholdValue125DisplayOnChart: contains(costs, 'thresholdValue125DisplayOnChart') - ? costs.thresholdValue125DisplayOnChart - : 'Disabled' - thresholdValue125SendNotificationWhenExceeded: contains(costs, 'thresholdValue125SendNotificationWhenExceeded') - ? costs.thresholdValue125SendNotificationWhenExceeded - : 'Disabled' + currencyCode: costs.?currencyCode ?? 'USD' + cycleType: costs!.cycleType + cycleStartDateTime: costs.?cycleStartDateTime + cycleEndDateTime: costs.?cycleEndDateTime + status: costs.?status ?? 'Enabled' + target: costs.?target ?? 0 + thresholdValue25DisplayOnChart: costs.?thresholdValue25DisplayOnChart ?? 'Disabled' + thresholdValue25SendNotificationWhenExceeded: costs.?thresholdValue25SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue50DisplayOnChart: costs.?thresholdValue50DisplayOnChart ?? 'Disabled' + thresholdValue50SendNotificationWhenExceeded: costs.?thresholdValue50SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue75DisplayOnChart: costs.?thresholdValue75DisplayOnChart ?? 'Disabled' + thresholdValue75SendNotificationWhenExceeded: costs.?thresholdValue75SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue100DisplayOnChart: costs.?thresholdValue100DisplayOnChart ?? 'Disabled' + thresholdValue100SendNotificationWhenExceeded: costs.?thresholdValue100SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue125DisplayOnChart: costs.?thresholdValue125DisplayOnChart ?? 'Disabled' + thresholdValue125SendNotificationWhenExceeded: costs.?thresholdValue125SendNotificationWhenExceeded ?? 'Disabled' } } @@ -439,3 +412,199 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +type artifactsourcesType = { + @description('Required. The name of the artifact source.') + name: string + + @description('Optional. The tags of the artifact source.') + tags: object? + + @description('Optional. The display name of the artifact source. Default is the name of the artifact source.') + displayName: string? + + @description('Optional. The artifact source\'s branch reference (e.g. main or master).') + branchRef: string? + + @description('Conditional. The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty.') + folderPath: string? + + @description('Conditional. The folder containing Azure Resource Manager templates. Required if "folderPath" is empty.') + armTemplateFolderPath: string? + + @description('Optional. The artifact source\'s type.') + sourceType: 'GitHub' | 'StorageAccount' | 'VsoGit'? + + @description('Optional. Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled".') + status: 'Enabled' | 'Disabled'? + + @description('Required. The artifact source\'s URI.') + uri: string + + @description('Optional. The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab\'s managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity.') + @secure() + securityToken: string? +}[]? + +import { allowedSubnetType, subnetOverrideType } from 'virtualnetwork/main.bicep' +type virtualNetworkType = { + @description('Required. The name of the virtual network.') + name: string + + @description('Optional. The tags of the virtual network.') + tags: object? + + @description('Required. The external provider resource ID of the virtual network.') + externalProviderResourceId: string + + @description('Optional. The description of the virtual network.') + description: string? + + @description('Optional. The allowed subnets of the virtual network.') + allowedSubnets: allowedSubnetType[]? + + @description('Optional. The subnet overrides of the virtual network.') + subnetOverrides: subnetOverrideType[]? +}[]? + +type costsType = { + @description('Optional. The tags of the resource.') + tags: object? + + @description('Required. Reporting cycle type.') + cycleType: 'Custom' | 'CalendarMonth' + + @description('Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') + cycleStartDateTime: string? + + @description('Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') + cycleEndDateTime: string? + + @description('Optional. Target cost status.') + status: 'Enabled' | 'Disabled'? + + @description('Optional. Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds.') + target: int? + + @description('Optional. The currency code of the cost. Default is "USD".') + currencyCode: string? + + @description('Optional. Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue25DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue25SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue50DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue50SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue75DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue75SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue100DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue100SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue125DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue125SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? +}? + +type notificationChannelType = { + @description('Required. The name of the notification channel.') + name: 'autoShutdown' | 'costThreshold' + + @description('Optional. The tags of the notification channel.') + tags: object? + + @description('Optional. The description of the notification.') + description: string? + + @description('Required. The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost".') + events: string[] + + @description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') + emailRecipient: string? + + @description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') + webHookUrl: string? + + @description('Optional. The locale to use when sending a notification (fallback for unsupported languages is EN).') + notificationLocale: string? +}[]? + +type policiesType = { + @description('Required. The name of the policy.') + name: string + + @description('Optional. The description of the policy.') + description: string? + + @description('Required. The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy).') + evaluatorType: 'AllowedValuesPolicy' | 'MaxValuePolicy' + + @description('Optional. The fact data of the policy.') + factData: string? + + @description('Required. The fact name of the policy.') + factName: + | 'EnvironmentTemplate' + | 'GalleryImage' + | 'LabPremiumVmCount' + | 'LabTargetCost' + | 'LabVmCount' + | 'LabVmSize' + | 'ScheduleEditPermission' + | 'UserOwnedLabPremiumVmCount' + | 'UserOwnedLabVmCount' + | 'UserOwnedLabVmCountInSubnet' + + @description('Optional. The status of the policy. Default is "Enabled".') + status: 'Disabled' | 'Enabled'? + + @description('Required. The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy).') + threshold: string +}[]? + +import { dailyRecurrenceType, hourlyRecurrenceType, notificationSettingsType, weeklyRecurrenceType } from 'schedule/main.bicep' +type scheduleType = { + @description('Required. The name of the schedule.') + name: 'LabVmsShutdown' | 'LabVmAutoStart' + + @description('Optional. The tags of the schedule.') + tags: object? + + @description('Required. The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask).') + taskType: 'LabVmsShutdownTask' | 'LabVmsStartupTask' + + @description('Optional. The daily recurrence of the schedule.') + dailyRecurrence: dailyRecurrenceType? + + @description('Optional. If the schedule will occur multiple times a day, specify the hourly recurrence.') + hourlyRecurrence: hourlyRecurrenceType? + + @description('Optional. If the schedule will occur only some days of the week, specify the weekly recurrence.') + weeklyRecurrence: weeklyRecurrenceType? + + @description('Optional. The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled".') + status: 'Disabled' | 'Enabled'? + + @description('Optional. The resource ID to which the schedule belongs.') + targetResourceId: string? + + @description('Optional. The time zone ID of the schedule. Defaults to "Pacific Standard time".') + timeZoneId: string? + + @description('Optional. The notification settings for the schedule.') + notificationSettings: notificationSettingsType? +}[]? diff --git a/avm/res/dev-test-lab/lab/main.json b/avm/res/dev-test-lab/lab/main.json index e43e8d6a81..711f287f08 100644 --- a/avm/res/dev-test-lab/lab/main.json +++ b/avm/res/dev-test-lab/lab/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18263140101339219429" + "templateHash": "9164793528274272981" }, "name": "DevTest Labs", "description": "This module deploys a DevTest Lab.", @@ -125,6 +125,777 @@ } }, "nullable": true + }, + "artifactsourcesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the artifact source." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the artifact source." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The display name of the artifact source. Default is the name of the artifact source." + } + }, + "branchRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The artifact source's branch reference (e.g. main or master)." + } + }, + "folderPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." + } + }, + "armTemplateFolderPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." + } + }, + "sourceType": { + "type": "string", + "allowedValues": [ + "GitHub", + "StorageAccount", + "VsoGit" + ], + "nullable": true, + "metadata": { + "description": "Optional. The artifact source's type." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is \"Enabled\"." + } + }, + "uri": { + "type": "string", + "metadata": { + "description": "Required. The artifact source's URI." + } + }, + "securityToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity." + } + } + } + }, + "nullable": true + }, + "virtualNetworkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual network." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the virtual network." + } + }, + "externalProviderResourceId": { + "type": "string", + "metadata": { + "description": "Required. The external provider resource ID of the virtual network." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the virtual network." + } + }, + "allowedSubnets": { + "type": "array", + "items": { + "$ref": "#/definitions/allowedSubnetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The allowed subnets of the virtual network." + } + }, + "subnetOverrides": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetOverrideType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The subnet overrides of the virtual network." + } + } + } + }, + "nullable": true + }, + "costsType": { + "type": "object", + "properties": { + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the resource." + } + }, + "cycleType": { + "type": "string", + "allowedValues": [ + "CalendarMonth", + "Custom" + ], + "metadata": { + "description": "Required. Reporting cycle type." + } + }, + "cycleStartDateTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." + } + }, + "cycleEndDateTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost status." + } + }, + "target": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Lab target cost (e.g. 100). The target cost will appear in the \"Cost trend\" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds." + } + }, + "currencyCode": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The currency code of the cost. Default is \"USD\"." + } + }, + "thresholdValue25DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue25SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue50DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue50SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue75DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue75SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue100DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue100SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue125DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue125SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + } + }, + "nullable": true + }, + "notificationChannelType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "autoShutdown", + "costThreshold" + ], + "metadata": { + "description": "Required. The name of the notification channel." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the notification channel." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the notification." + } + }, + "events": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of event for which this notification is enabled. Can be \"AutoShutdown\" or \"Cost\"." + } + }, + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + } + } + }, + "nullable": true + }, + "policiesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the policy." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the policy." + } + }, + "evaluatorType": { + "type": "string", + "allowedValues": [ + "AllowedValuesPolicy", + "MaxValuePolicy" + ], + "metadata": { + "description": "Required. The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy)." + } + }, + "factData": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The fact data of the policy." + } + }, + "factName": { + "type": "string", + "allowedValues": [ + "EnvironmentTemplate", + "GalleryImage", + "LabPremiumVmCount", + "LabTargetCost", + "LabVmCount", + "LabVmSize", + "ScheduleEditPermission", + "UserOwnedLabPremiumVmCount", + "UserOwnedLabVmCount", + "UserOwnedLabVmCountInSubnet" + ], + "metadata": { + "description": "Required. The fact name of the policy." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the policy. Default is \"Enabled\"." + } + }, + "threshold": { + "type": "string", + "metadata": { + "description": "Required. The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy)." + } + } + } + }, + "nullable": true + }, + "scheduleType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "LabVmAutoStart", + "LabVmsShutdown" + ], + "metadata": { + "description": "Required. The name of the schedule." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the schedule." + } + }, + "taskType": { + "type": "string", + "allowedValues": [ + "LabVmsShutdownTask", + "LabVmsStartupTask" + ], + "metadata": { + "description": "Required. The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask)." + } + }, + "dailyRecurrence": { + "$ref": "#/definitions/dailyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. The daily recurrence of the schedule." + } + }, + "hourlyRecurrence": { + "$ref": "#/definitions/hourlyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." + } + }, + "weeklyRecurrence": { + "$ref": "#/definitions/weeklyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the schedule (i.e. Enabled, Disabled). Default is \"Enabled\"." + } + }, + "targetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID to which the schedule belongs." + } + }, + "timeZoneId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time zone ID of the schedule. Defaults to \"Pacific Standard time\"." + } + }, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The notification settings for the schedule." + } + } + } + }, + "nullable": true + }, + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "virtualnetwork/main.bicep" + } + } + }, + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "virtualnetwork/main.bicep" + } + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } } }, "parameters": { @@ -239,7 +1010,7 @@ "managedIdentities": { "$ref": "#/definitions/managedIdentitiesType", "metadata": { - "description": "Optional. The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment." + "description": "Optional. The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab." } }, "managementIdentitiesResourceIds": { @@ -307,43 +1078,37 @@ } }, "virtualnetworks": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/virtualNetworkType", "metadata": { "description": "Optional. Virtual networks to create for the lab." } }, "policies": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/policiesType", "metadata": { "description": "Optional. Policies to create for the lab." } }, "schedules": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/scheduleType", "metadata": { "description": "Optional. Schedules to create for the lab." } }, "notificationchannels": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/notificationChannelType", "metadata": { "description": "Conditional. Notification Channels to create for the lab. Required if the schedules property \"notificationSettingsStatus\" is set to \"Enabled." } }, "artifactsources": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/artifactsourcesType", "metadata": { "description": "Optional. Artifact sources to create for the lab." } }, "costs": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/costsType", "metadata": { "description": "Optional. Costs to create for the lab." } @@ -365,7 +1130,7 @@ } ], "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), createObject('type', 'SystemAssigned'))]", "formattedManagementIdentities": "[if(not(empty(parameters('managementIdentitiesResourceIds'))), reduce(map(coalesce(parameters('managementIdentitiesResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next')))), createObject())]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -466,7 +1231,7 @@ "lab_virtualNetworks": { "copy": { "name": "lab_virtualNetworks", - "count": "[length(parameters('virtualnetworks'))]" + "count": "[length(coalesce(parameters('virtualnetworks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -481,17 +1246,23 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('virtualnetworks')[copyIndex()].name]" + "value": "[coalesce(parameters('virtualnetworks'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('virtualnetworks')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "externalProviderResourceId": { - "value": "[parameters('virtualnetworks')[copyIndex()].externalProviderResourceId]" + "value": "[coalesce(parameters('virtualnetworks'), createArray())[copyIndex()].externalProviderResourceId]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'description')]" + }, + "allowedSubnets": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'allowedSubnets')]" }, - "description": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'description'), createObject('value', parameters('virtualnetworks')[copyIndex()].description), createObject('value', ''))]", - "allowedSubnets": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'allowedSubnets'), createObject('value', parameters('virtualnetworks')[copyIndex()].allowedSubnets), createObject('value', createArray()))]", - "subnetOverrides": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'subnetOverrides'), createObject('value', parameters('virtualnetworks')[copyIndex()].subnetOverrides), createObject('value', createArray()))]" + "subnetOverrides": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'subnetOverrides')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -501,12 +1272,132 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1030641179919111475" + "templateHash": "18309816581107210302" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -610,7 +1501,7 @@ "lab_policies": { "copy": { "name": "lab_policies", - "count": "[length(parameters('policies'))]" + "count": "[length(coalesce(parameters('policies'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -625,19 +1516,25 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('policies')[copyIndex()].name]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].name]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'description')]" }, - "description": "[if(contains(parameters('policies')[copyIndex()], 'description'), createObject('value', parameters('policies')[copyIndex()].description), createObject('value', ''))]", "evaluatorType": { - "value": "[parameters('policies')[copyIndex()].evaluatorType]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].evaluatorType]" + }, + "factData": { + "value": "[tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'factData')]" }, - "factData": "[if(contains(parameters('policies')[copyIndex()], 'factData'), createObject('value', parameters('policies')[copyIndex()].factData), createObject('value', ''))]", "factName": { - "value": "[parameters('policies')[copyIndex()].factName]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].factName]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'status'), 'Enabled')]" }, - "status": "[if(contains(parameters('policies')[copyIndex()], 'status'), createObject('value', parameters('policies')[copyIndex()].status), createObject('value', 'Enabled'))]", "threshold": { - "value": "[parameters('policies')[copyIndex()].threshold]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].threshold]" } }, "template": { @@ -647,10 +1544,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12174587300527865958" + "templateHash": "10307787353498465860" }, "name": "DevTest Lab Policy Sets Policies", - "description": "This module deploys a DevTest Lab Policy Sets Policy.\n\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", + "description": "This module deploys a DevTest Lab Policy Sets Policy.\r\n\r\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -732,9 +1629,9 @@ "apiVersion": "2018-09-15", "name": "[format('{0}/{1}/{2}', parameters('labName'), 'default', parameters('name'))]", "properties": { - "description": "[parameters('description')]", + "description": "[coalesce(parameters('description'), '')]", "evaluatorType": "[parameters('evaluatorType')]", - "factData": "[parameters('factData')]", + "factData": "[coalesce(parameters('factData'), '')]", "factName": "[parameters('factName')]", "status": "[parameters('status')]", "threshold": "[parameters('threshold')]" @@ -773,7 +1670,7 @@ "lab_schedules": { "copy": { "name": "lab_schedules", - "count": "[length(parameters('schedules'))]" + "count": "[length(coalesce(parameters('schedules'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -788,22 +1685,35 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('schedules')[copyIndex()].name]" + "value": "[coalesce(parameters('schedules'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('schedules')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "taskType": { - "value": "[parameters('schedules')[copyIndex()].taskType]" - }, - "dailyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'dailyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].dailyRecurrence), createObject('value', createObject()))]", - "hourlyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'hourlyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].hourlyRecurrence), createObject('value', createObject()))]", - "weeklyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'weeklyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].weeklyRecurrence), createObject('value', createObject()))]", - "status": "[if(contains(parameters('schedules')[copyIndex()], 'status'), createObject('value', parameters('schedules')[copyIndex()].status), createObject('value', 'Enabled'))]", - "targetResourceId": "[if(contains(parameters('schedules')[copyIndex()], 'targetResourceId'), createObject('value', parameters('schedules')[copyIndex()].targetResourceId), createObject('value', ''))]", - "timeZoneId": "[if(contains(parameters('schedules')[copyIndex()], 'timeZoneId'), createObject('value', parameters('schedules')[copyIndex()].timeZoneId), createObject('value', 'Pacific Standard time'))]", - "notificationSettingsStatus": "[if(contains(parameters('schedules')[copyIndex()], 'notificationSettingsStatus'), createObject('value', parameters('schedules')[copyIndex()].notificationSettingsStatus), createObject('value', 'Disabled'))]", - "notificationSettingsTimeInMinutes": "[if(contains(parameters('schedules')[copyIndex()], 'notificationSettingsTimeInMinutes'), createObject('value', parameters('schedules')[copyIndex()].notificationSettingsTimeInMinutes), createObject('value', 30))]" + "value": "[coalesce(parameters('schedules'), createArray())[copyIndex()].taskType]" + }, + "dailyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'dailyRecurrence')]" + }, + "hourlyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'hourlyRecurrence')]" + }, + "weeklyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'weeklyRecurrence')]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'status'), 'Enabled')]" + }, + "targetResourceId": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'targetResourceId')]" + }, + "timeZoneId": { + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'timeZoneId'), 'Pacific Standard time')]" + }, + "notificationSettings": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'notificationSettings')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -813,12 +1723,116 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12081847452316418446" + "templateHash": "9010276477624635732" }, "name": "DevTest Lab Schedules", - "description": "This module deploys a DevTest Lab Schedule.\n\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", + "description": "This module deploys a DevTest Lab Schedule.\r\n\r\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -854,22 +1868,19 @@ } }, "dailyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/dailyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur once each day of the week, specify the daily recurrence." } }, "hourlyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/hourlyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." } }, "weeklyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/weeklyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." } @@ -887,7 +1898,7 @@ }, "targetResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID to which the schedule belongs." } @@ -899,22 +1910,10 @@ "description": "Optional. The time zone ID (e.g. Pacific Standard time)." } }, - "notificationSettingsStatus": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled)." - } - }, - "notificationSettingsTimeInMinutes": { - "type": "int", - "defaultValue": 30, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", "metadata": { - "description": "Optional. Time in minutes before event at which notification will be sent. Optional if \"notificationSettingsStatus\" is set to \"Enabled\". Default is 30 minutes." + "description": "Optional. The notification settings for the schedule." } } }, @@ -932,13 +1931,13 @@ "tags": "[parameters('tags')]", "properties": { "taskType": "[parameters('taskType')]", - "dailyRecurrence": "[if(not(empty(parameters('dailyRecurrence'))), parameters('dailyRecurrence'), null())]", - "hourlyRecurrence": "[if(not(empty(parameters('hourlyRecurrence'))), parameters('hourlyRecurrence'), null())]", - "weeklyRecurrence": "[if(not(empty(parameters('weeklyRecurrence'))), parameters('weeklyRecurrence'), null())]", + "dailyRecurrence": "[parameters('dailyRecurrence')]", + "hourlyRecurrence": "[parameters('hourlyRecurrence')]", + "weeklyRecurrence": "[parameters('weeklyRecurrence')]", "status": "[parameters('status')]", - "targetResourceId": "[if(not(empty(parameters('targetResourceId'))), parameters('targetResourceId'), null())]", + "targetResourceId": "[parameters('targetResourceId')]", "timeZoneId": "[parameters('timeZoneId')]", - "notificationSettings": "[if(equals(parameters('notificationSettingsStatus'), 'Enabled'), createObject('status', parameters('notificationSettingsStatus'), 'timeInMinutes', parameters('notificationSettingsTimeInMinutes')), createObject())]" + "notificationSettings": "[parameters('notificationSettings')]" }, "dependsOn": [ "lab" @@ -977,7 +1976,7 @@ "lab_notificationChannels": { "copy": { "name": "lab_notificationChannels", - "count": "[length(parameters('notificationchannels'))]" + "count": "[length(coalesce(parameters('notificationchannels'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -992,18 +1991,26 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('notificationchannels')[copyIndex()].name]" + "value": "[coalesce(parameters('notificationchannels'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('notificationchannels')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'description')]" }, - "description": "[if(contains(parameters('notificationchannels')[copyIndex()], 'description'), createObject('value', parameters('notificationchannels')[copyIndex()].description), createObject('value', ''))]", "events": { - "value": "[parameters('notificationchannels')[copyIndex()].events]" + "value": "[coalesce(parameters('notificationchannels'), createArray())[copyIndex()].events]" + }, + "emailRecipient": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'emailRecipient')]" }, - "emailRecipient": "[if(contains(parameters('notificationchannels')[copyIndex()], 'emailRecipient'), createObject('value', parameters('notificationchannels')[copyIndex()].emailRecipient), createObject('value', ''))]", - "webHookUrl": "[if(contains(parameters('notificationchannels')[copyIndex()], 'webhookUrl'), createObject('value', parameters('notificationchannels')[copyIndex()].webhookUrl), createObject('value', ''))]", - "notificationLocale": "[if(contains(parameters('notificationchannels')[copyIndex()], 'notificationLocale'), createObject('value', parameters('notificationchannels')[copyIndex()].notificationLocale), createObject('value', 'en'))]" + "webHookUrl": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'webHookUrl')]" + }, + "notificationLocale": { + "value": "[coalesce(tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'notificationLocale'), 'en')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1013,10 +2020,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7230564820615005386" + "templateHash": "95632191903979650" }, "name": "DevTest Lab Notification Channels", - "description": "This module deploys a DevTest Lab Notification Channel.\n\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", + "description": "This module deploys a DevTest Lab Notification Channel.\r\n\r\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1059,7 +2066,7 @@ }, "emailRecipient": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." } @@ -1092,8 +2099,16 @@ "name": "[format('{0}/{1}', parameters('labName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { + "copy": [ + { + "name": "events", + "count": "[length(parameters('events'))]", + "input": { + "eventName": "[parameters('events')[copyIndex('events')]]" + } + } + ], "description": "[parameters('description')]", - "events": "[parameters('events')]", "emailRecipient": "[parameters('emailRecipient')]", "webHookUrl": "[parameters('webHookUrl')]", "notificationLocale": "[parameters('notificationLocale')]" @@ -1135,7 +2150,7 @@ "lab_artifactSources": { "copy": { "name": "lab_artifactSources", - "count": "[length(parameters('artifactsources'))]" + "count": "[length(coalesce(parameters('artifactsources'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1150,19 +2165,34 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('artifactsources')[copyIndex()].name]" + "value": "[coalesce(parameters('artifactsources'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('artifactsources')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "displayName": "[if(contains(parameters('artifactsources')[copyIndex()], 'displayName'), createObject('value', parameters('artifactsources')[copyIndex()].displayName), createObject('value', parameters('artifactsources')[copyIndex()].name))]", - "branchRef": "[if(contains(parameters('artifactsources')[copyIndex()], 'branchRef'), createObject('value', parameters('artifactsources')[copyIndex()].branchRef), createObject('value', ''))]", - "folderPath": "[if(contains(parameters('artifactsources')[copyIndex()], 'folderPath'), createObject('value', parameters('artifactsources')[copyIndex()].folderPath), createObject('value', ''))]", - "armTemplateFolderPath": "[if(contains(parameters('artifactsources')[copyIndex()], 'armTemplateFolderPath'), createObject('value', parameters('artifactsources')[copyIndex()].armTemplateFolderPath), createObject('value', ''))]", - "sourceType": "[if(contains(parameters('artifactsources')[copyIndex()], 'sourceType'), createObject('value', parameters('artifactsources')[copyIndex()].sourceType), createObject('value', ''))]", - "status": "[if(contains(parameters('artifactsources')[copyIndex()], 'status'), createObject('value', parameters('artifactsources')[copyIndex()].status), createObject('value', 'Enabled'))]", + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "displayName": { + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'displayName'), coalesce(parameters('artifactsources'), createArray())[copyIndex()].name)]" + }, + "branchRef": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'branchRef')]" + }, + "folderPath": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'folderPath')]" + }, + "armTemplateFolderPath": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'armTemplateFolderPath')]" + }, + "sourceType": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'sourceType')]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'status'), 'Enabled')]" + }, "uri": { - "value": "[parameters('artifactsources')[copyIndex()].uri]" + "value": "[coalesce(parameters('artifactsources'), createArray())[copyIndex()].uri]" + }, + "securityToken": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'securityToken')]" } }, "template": { @@ -1173,10 +2203,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15552685312013632487" + "templateHash": "13904061272597362111" }, "name": "DevTest Lab Artifact Sources", - "description": "This module deploys a DevTest Lab Artifact Source.\n\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", + "description": "This module deploys a DevTest Lab Artifact Source.\r\n\r\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1208,37 +2238,36 @@ }, "branchRef": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The artifact source's branch reference (e.g. main or master)." } }, "folderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." } }, "armTemplateFolderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." } }, "securityToken": { "type": "securestring", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The security token to authenticate to the artifact source." } }, "sourceType": { "type": "string", - "defaultValue": "", + "nullable": true, "allowedValues": [ - "", "GitHub", "StorageAccount", "VsoGit" @@ -1279,11 +2308,11 @@ "tags": "[parameters('tags')]", "properties": { "displayName": "[parameters('displayName')]", - "branchRef": "[if(not(empty(parameters('branchRef'))), parameters('branchRef'), null())]", - "folderPath": "[if(not(empty(parameters('folderPath'))), parameters('folderPath'), null())]", - "armTemplateFolderPath": "[if(not(empty(parameters('armTemplateFolderPath'))), parameters('armTemplateFolderPath'), null())]", - "securityToken": "[if(not(empty(parameters('securityToken'))), parameters('securityToken'), null())]", - "sourceType": "[if(not(empty(parameters('sourceType'))), parameters('sourceType'), null())]", + "branchRef": "[parameters('branchRef')]", + "folderPath": "[parameters('folderPath')]", + "armTemplateFolderPath": "[parameters('armTemplateFolderPath')]", + "securityToken": "[parameters('securityToken')]", + "sourceType": "[parameters('sourceType')]", "status": "[parameters('status')]", "uri": "[parameters('uri')]" }, @@ -1338,24 +2367,54 @@ "tags": { "value": "[coalesce(tryGet(parameters('costs'), 'tags'), parameters('tags'))]" }, - "currencyCode": "[if(contains(parameters('costs'), 'currencyCode'), createObject('value', parameters('costs').currencyCode), createObject('value', 'USD'))]", + "currencyCode": { + "value": "[coalesce(tryGet(parameters('costs'), 'currencyCode'), 'USD')]" + }, "cycleType": { "value": "[parameters('costs').cycleType]" }, - "cycleStartDateTime": "[if(contains(parameters('costs'), 'cycleStartDateTime'), createObject('value', parameters('costs').cycleStartDateTime), createObject('value', ''))]", - "cycleEndDateTime": "[if(contains(parameters('costs'), 'cycleEndDateTime'), createObject('value', parameters('costs').cycleEndDateTime), createObject('value', ''))]", - "status": "[if(contains(parameters('costs'), 'status'), createObject('value', parameters('costs').status), createObject('value', 'Enabled'))]", - "target": "[if(contains(parameters('costs'), 'target'), createObject('value', parameters('costs').target), createObject('value', 0))]", - "thresholdValue25DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue25DisplayOnChart'), createObject('value', parameters('costs').thresholdValue25DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue25SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue25SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue25SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue50DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue50DisplayOnChart'), createObject('value', parameters('costs').thresholdValue50DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue50SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue50SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue50SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue75DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue75DisplayOnChart'), createObject('value', parameters('costs').thresholdValue75DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue75SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue75SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue75SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue100DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue100DisplayOnChart'), createObject('value', parameters('costs').thresholdValue100DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue100SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue100SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue100SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue125DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue125DisplayOnChart'), createObject('value', parameters('costs').thresholdValue125DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue125SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue125SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue125SendNotificationWhenExceeded), createObject('value', 'Disabled'))]" + "cycleStartDateTime": { + "value": "[tryGet(parameters('costs'), 'cycleStartDateTime')]" + }, + "cycleEndDateTime": { + "value": "[tryGet(parameters('costs'), 'cycleEndDateTime')]" + }, + "status": { + "value": "[coalesce(tryGet(parameters('costs'), 'status'), 'Enabled')]" + }, + "target": { + "value": "[coalesce(tryGet(parameters('costs'), 'target'), 0)]" + }, + "thresholdValue25DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue25DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue25SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue25SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue50DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue50DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue50SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue50SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue75DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue75DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue75SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue75SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue100DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue100DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue100SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue100SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue125DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue125DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue125SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue125SendNotificationWhenExceeded'), 'Disabled')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1365,10 +2424,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16348143074487982445" + "templateHash": "7509251296299887127" }, "name": "DevTest Lab Costs", - "description": "This module deploys a DevTest Lab Cost.\n\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", + "description": "This module deploys a DevTest Lab Cost.\r\n\r\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1397,14 +2456,14 @@ }, "cycleStartDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } }, "cycleEndDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } diff --git a/avm/res/dev-test-lab/lab/notificationchannel/README.md b/avm/res/dev-test-lab/lab/notificationchannel/README.md index 5ab3d187aa..9fee767321 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/README.md +++ b/avm/res/dev-test-lab/lab/notificationchannel/README.md @@ -71,7 +71,6 @@ The email recipient to send notifications to (can be a list of semi-colon separa - Required: No - Type: string -- Default: `''` ### Parameter: `labName` diff --git a/avm/res/dev-test-lab/lab/notificationchannel/main.bicep b/avm/res/dev-test-lab/lab/notificationchannel/main.bicep index 2e97b66ddd..84fa6aa9f8 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/main.bicep +++ b/avm/res/dev-test-lab/lab/notificationchannel/main.bicep @@ -24,7 +24,7 @@ param description string = '' param events array = [] @sys.description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') -param emailRecipient string = '' +param emailRecipient string? @sys.description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') param webHookUrl string = '' @@ -42,7 +42,11 @@ resource notificationChannel 'Microsoft.DevTestLab/labs/notificationchannels@201 tags: tags properties: { description: description - events: events + events: [ + for event in events: { + eventName: event + } + ] emailRecipient: emailRecipient webHookUrl: webHookUrl notificationLocale: notificationLocale diff --git a/avm/res/dev-test-lab/lab/notificationchannel/main.json b/avm/res/dev-test-lab/lab/notificationchannel/main.json index f7dbaa7d25..36963586b6 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/main.json +++ b/avm/res/dev-test-lab/lab/notificationchannel/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7230564820615005386" + "templateHash": "95632191903979650" }, "name": "DevTest Lab Notification Channels", - "description": "This module deploys a DevTest Lab Notification Channel.\n\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", + "description": "This module deploys a DevTest Lab Notification Channel.\r\n\r\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -52,7 +52,7 @@ }, "emailRecipient": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." } @@ -85,8 +85,16 @@ "name": "[format('{0}/{1}', parameters('labName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { + "copy": [ + { + "name": "events", + "count": "[length(parameters('events'))]", + "input": { + "eventName": "[parameters('events')[copyIndex('events')]]" + } + } + ], "description": "[parameters('description')]", - "events": "[parameters('events')]", "emailRecipient": "[parameters('emailRecipient')]", "webHookUrl": "[parameters('webHookUrl')]", "notificationLocale": "[parameters('notificationLocale')]" diff --git a/avm/res/dev-test-lab/lab/policyset/policy/main.bicep b/avm/res/dev-test-lab/lab/policyset/policy/main.bicep index c9dbe3dba8..a4a53a50c4 100644 --- a/avm/res/dev-test-lab/lab/policyset/policy/main.bicep +++ b/avm/res/dev-test-lab/lab/policyset/policy/main.bicep @@ -60,9 +60,9 @@ resource policy 'Microsoft.DevTestLab/labs/policysets/policies@2018-09-15' = { name: name parent: lab::policySets properties: { - description: description + description: description ?? '' evaluatorType: evaluatorType - factData: factData + factData: factData ?? '' factName: factName status: status threshold: threshold diff --git a/avm/res/dev-test-lab/lab/policyset/policy/main.json b/avm/res/dev-test-lab/lab/policyset/policy/main.json index 3a63fb4c36..35370542da 100644 --- a/avm/res/dev-test-lab/lab/policyset/policy/main.json +++ b/avm/res/dev-test-lab/lab/policyset/policy/main.json @@ -5,10 +5,10 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12174587300527865958" + "templateHash": "10307787353498465860" }, "name": "DevTest Lab Policy Sets Policies", - "description": "This module deploys a DevTest Lab Policy Sets Policy.\n\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", + "description": "This module deploys a DevTest Lab Policy Sets Policy.\r\n\r\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -90,9 +90,9 @@ "apiVersion": "2018-09-15", "name": "[format('{0}/{1}/{2}', parameters('labName'), 'default', parameters('name'))]", "properties": { - "description": "[parameters('description')]", + "description": "[coalesce(parameters('description'), '')]", "evaluatorType": "[parameters('evaluatorType')]", - "factData": "[parameters('factData')]", + "factData": "[coalesce(parameters('factData'), '')]", "factName": "[parameters('factName')]", "status": "[parameters('status')]", "threshold": "[parameters('threshold')]" diff --git a/avm/res/dev-test-lab/lab/schedule/README.md b/avm/res/dev-test-lab/lab/schedule/README.md index dda473fdde..2358f8c145 100644 --- a/avm/res/dev-test-lab/lab/schedule/README.md +++ b/avm/res/dev-test-lab/lab/schedule/README.md @@ -39,8 +39,7 @@ Lab schedules are used to modify the settings for auto-shutdown, auto-start for | :-- | :-- | :-- | | [`dailyRecurrence`](#parameter-dailyrecurrence) | object | If the schedule will occur once each day of the week, specify the daily recurrence. | | [`hourlyRecurrence`](#parameter-hourlyrecurrence) | object | If the schedule will occur multiple times a day, specify the hourly recurrence. | -| [`notificationSettingsStatus`](#parameter-notificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). | -| [`notificationSettingsTimeInMinutes`](#parameter-notificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes. | +| [`notificationSettings`](#parameter-notificationsettings) | object | The notification settings for the schedule. | | [`status`](#parameter-status) | string | The status of the schedule (i.e. Enabled, Disabled). | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`targetResourceId`](#parameter-targetresourceid) | string | The resource ID to which the schedule belongs. | @@ -88,7 +87,19 @@ If the schedule will occur once each day of the week, specify the daily recurren - Required: No - Type: object -- Default: `{}` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-dailyrecurrencetime) | string | The time of day the schedule will occur. | + +### Parameter: `dailyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string ### Parameter: `hourlyRecurrence` @@ -96,15 +107,69 @@ If the schedule will occur multiple times a day, specify the hourly recurrence. - Required: No - Type: object -- Default: `{}` -### Parameter: `notificationSettingsStatus` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`minute`](#parameter-hourlyrecurrenceminute) | int | Minutes of the hour the schedule will run. | + +### Parameter: `hourlyRecurrence.minute` + +Minutes of the hour the schedule will run. + +- Required: Yes +- Type: int + +### Parameter: `notificationSettings` + +The notification settings for the schedule. + +- Required: No +- Type: object + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-notificationsettingsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-notificationsettingswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`notificationLocale`](#parameter-notificationsettingsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`status`](#parameter-notificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. | +| [`timeInMinutes`](#parameter-notificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. | + +### Parameter: `notificationSettings.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationSettings.webHookUrl` + +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationSettings.notificationLocale` + +The locale to use when sending a notification (fallback for unsupported languages is EN). + +- Required: No +- Type: string + +### Parameter: `notificationSettings.status` -If notifications are enabled for this schedule (i.e. Enabled, Disabled). +If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. - Required: No - Type: string -- Default: `'Disabled'` - Allowed: ```Bicep [ @@ -113,13 +178,12 @@ If notifications are enabled for this schedule (i.e. Enabled, Disabled). ] ``` -### Parameter: `notificationSettingsTimeInMinutes` +### Parameter: `notificationSettings.timeInMinutes` -Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes. +Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. - Required: No - Type: int -- Default: `30` ### Parameter: `status` @@ -149,7 +213,6 @@ The resource ID to which the schedule belongs. - Required: No - Type: string -- Default: `''` ### Parameter: `timeZoneId` @@ -165,7 +228,27 @@ If the schedule will occur only some days of the week, specify the weekly recurr - Required: No - Type: object -- Default: `{}` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-weeklyrecurrencetime) | string | The time of day the schedule will occur. | +| [`weekdays`](#parameter-weeklyrecurrenceweekdays) | array | The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). | + +### Parameter: `weeklyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `weeklyRecurrence.weekdays` + +The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). + +- Required: Yes +- Type: array ## Outputs diff --git a/avm/res/dev-test-lab/lab/schedule/main.bicep b/avm/res/dev-test-lab/lab/schedule/main.bicep index c71be08e40..fea3cc182f 100644 --- a/avm/res/dev-test-lab/lab/schedule/main.bicep +++ b/avm/res/dev-test-lab/lab/schedule/main.bicep @@ -25,13 +25,13 @@ param taskType string param tags object? @sys.description('Optional. If the schedule will occur once each day of the week, specify the daily recurrence.') -param dailyRecurrence object = {} +param dailyRecurrence dailyRecurrenceType @sys.description('Optional. If the schedule will occur multiple times a day, specify the hourly recurrence.') -param hourlyRecurrence object = {} +param hourlyRecurrence hourlyRecurrenceType @sys.description('Optional. If the schedule will occur only some days of the week, specify the weekly recurrence.') -param weeklyRecurrence object = {} +param weeklyRecurrence weeklyRecurrenceType @allowed([ 'Enabled' @@ -41,20 +41,13 @@ param weeklyRecurrence object = {} param status string = 'Enabled' @sys.description('Optional. The resource ID to which the schedule belongs.') -param targetResourceId string = '' +param targetResourceId string? @sys.description('Optional. The time zone ID (e.g. Pacific Standard time).') param timeZoneId string = 'Pacific Standard time' -@allowed([ - 'Enabled' - 'Disabled' -]) -@sys.description('Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled).') -param notificationSettingsStatus string = 'Disabled' - -@sys.description('Optional. Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes.') -param notificationSettingsTimeInMinutes int = 30 +@sys.description('Optional. The notification settings for the schedule.') +param notificationSettings notificationSettingsType resource lab 'Microsoft.DevTestLab/labs@2018-09-15' existing = { name: labName @@ -66,18 +59,13 @@ resource schedule 'Microsoft.DevTestLab/labs/schedules@2018-09-15' = { tags: tags properties: { taskType: taskType - dailyRecurrence: !empty(dailyRecurrence) ? dailyRecurrence : null - hourlyRecurrence: !empty(hourlyRecurrence) ? hourlyRecurrence : null - weeklyRecurrence: !empty(weeklyRecurrence) ? weeklyRecurrence : null + dailyRecurrence: dailyRecurrence + hourlyRecurrence: hourlyRecurrence + weeklyRecurrence: weeklyRecurrence status: status - targetResourceId: !empty(targetResourceId) ? targetResourceId : null + targetResourceId: targetResourceId timeZoneId: timeZoneId - notificationSettings: notificationSettingsStatus == 'Enabled' - ? { - status: notificationSettingsStatus - timeInMinutes: notificationSettingsTimeInMinutes - } - : {} + notificationSettings: notificationSettings } } @@ -89,3 +77,46 @@ output resourceId string = schedule.id @sys.description('The name of the resource group the schedule was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +type dailyRecurrenceType = { + @sys.description('Required. The time of day the schedule will occur.') + time: string +}? + +@export() +type hourlyRecurrenceType = { + @sys.description('Required. Minutes of the hour the schedule will run.') + minute: int +}? + +@export() +type weeklyRecurrenceType = { + @sys.description('Required. The time of day the schedule will occur.') + time: string + + @sys.description('Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.).') + weekdays: string[] +}? + +@export() +type notificationSettingsType = { + @description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') + emailRecipient: string? + + @description('Optional. The locale to use when sending a notification (fallback for unsupported languages is EN).') + notificationLocale: string? + + @sys.description('Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled.') + status: 'Disabled' | 'Enabled'? + + @sys.description('Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified.') + timeInMinutes: int? + + @description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') + webHookUrl: string? +}? diff --git a/avm/res/dev-test-lab/lab/schedule/main.json b/avm/res/dev-test-lab/lab/schedule/main.json index 7b20dd6385..2b95e09f31 100644 --- a/avm/res/dev-test-lab/lab/schedule/main.json +++ b/avm/res/dev-test-lab/lab/schedule/main.json @@ -6,12 +6,116 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12081847452316418446" + "templateHash": "9010276477624635732" }, "name": "DevTest Lab Schedules", - "description": "This module deploys a DevTest Lab Schedule.\n\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", + "description": "This module deploys a DevTest Lab Schedule.\r\n\r\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -47,22 +151,19 @@ } }, "dailyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/dailyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur once each day of the week, specify the daily recurrence." } }, "hourlyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/hourlyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." } }, "weeklyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/weeklyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." } @@ -80,7 +181,7 @@ }, "targetResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID to which the schedule belongs." } @@ -92,22 +193,10 @@ "description": "Optional. The time zone ID (e.g. Pacific Standard time)." } }, - "notificationSettingsStatus": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled)." - } - }, - "notificationSettingsTimeInMinutes": { - "type": "int", - "defaultValue": 30, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", "metadata": { - "description": "Optional. Time in minutes before event at which notification will be sent. Optional if \"notificationSettingsStatus\" is set to \"Enabled\". Default is 30 minutes." + "description": "Optional. The notification settings for the schedule." } } }, @@ -125,13 +214,13 @@ "tags": "[parameters('tags')]", "properties": { "taskType": "[parameters('taskType')]", - "dailyRecurrence": "[if(not(empty(parameters('dailyRecurrence'))), parameters('dailyRecurrence'), null())]", - "hourlyRecurrence": "[if(not(empty(parameters('hourlyRecurrence'))), parameters('hourlyRecurrence'), null())]", - "weeklyRecurrence": "[if(not(empty(parameters('weeklyRecurrence'))), parameters('weeklyRecurrence'), null())]", + "dailyRecurrence": "[parameters('dailyRecurrence')]", + "hourlyRecurrence": "[parameters('hourlyRecurrence')]", + "weeklyRecurrence": "[parameters('weeklyRecurrence')]", "status": "[parameters('status')]", - "targetResourceId": "[if(not(empty(parameters('targetResourceId'))), parameters('targetResourceId'), null())]", + "targetResourceId": "[parameters('targetResourceId')]", "timeZoneId": "[parameters('timeZoneId')]", - "notificationSettings": "[if(equals(parameters('notificationSettingsStatus'), 'Enabled'), createObject('status', parameters('notificationSettingsStatus'), 'timeInMinutes', parameters('notificationSettingsTimeInMinutes')), createObject())]" + "notificationSettings": "[parameters('notificationSettings')]" }, "dependsOn": [ "lab" diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep index dcab1da10f..65d06f3e05 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep @@ -226,8 +226,10 @@ module testDeployment '../../../main.bicep' = [ dailyRecurrence: { time: '0000' } - notificationSettingsStatus: 'Enabled' - notificationSettingsTimeInMinutes: 30 + notificationSettings: { + status: 'Enabled' + timeInMinutes: 30 + } } { name: 'LabVmAutoStart' @@ -251,9 +253,7 @@ module testDeployment '../../../main.bicep' = [ name: 'autoShutdown' description: 'Integration configured for auto-shutdown' events: [ - { - eventName: 'AutoShutdown' - } + 'AutoShutdown' ] emailRecipient: 'mail@contosodtlmail.com' webHookUrl: 'https://webhook.contosotest.com' @@ -262,9 +262,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'costThreshold' events: [ - { - eventName: 'Cost' - } + 'Cost' ] webHookUrl: 'https://webhook.contosotest.com' } @@ -273,10 +271,9 @@ module testDeployment '../../../main.bicep' = [ { name: 'Public Repo' displayName: 'Public Artifact Repo' - status: 'Disabled' + status: 'Enabled' uri: 'https://github.com/Azure/azure-devtestlab.git' sourceType: 'GitHub' - branchRef: 'master' folderPath: '/Artifacts' } { @@ -287,12 +284,28 @@ module testDeployment '../../../main.bicep' = [ sourceType: 'GitHub' branchRef: 'master' armTemplateFolderPath: '/Environments' + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'DevTest Lab' + labName: '${namePrefix}${serviceShort}001' + } + } + { + name: 'Private Repo' + displayName: 'Private Artifact Repo' + status: 'Disabled' + uri: 'https://github.com/Azure/azure-devtestlab.git' + folderPath: '/Artifacts' + armTemplateFolderPath: '/ArmTemplates' + branchRef: 'main' + securityToken: guid(baseTime) } ] costs: { status: 'Enabled' cycleType: 'CalendarMonth' target: 450 + currencyCode: 'AUD' thresholdValue100DisplayOnChart: 'Enabled' thresholdValue100SendNotificationWhenExceeded: 'Enabled' } diff --git a/avm/res/dev-test-lab/lab/version.json b/avm/res/dev-test-lab/lab/version.json index 1c035df49f..b3d560b1ad 100644 --- a/avm/res/dev-test-lab/lab/version.json +++ b/avm/res/dev-test-lab/lab/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep index b73bab89b6..07e8f88432 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep @@ -49,3 +49,49 @@ output resourceId string = virtualNetwork.id @sys.description('The name of the resource group the lab virtual network was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +type allowedSubnetType = { + @sys.description('Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)).') + allowPublicIp: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Required. The resource ID of the allowed subnet.') + resourceId: string + + @sys.description('Required. The name of the subnet as seen in the lab.') + labSubnetName: string +} + +@export() +type subnetOverrideType = { + @sys.description('Required. The name given to the subnet within the lab.') + labSubnetName: string + + @sys.description('Required. The resource ID of the subnet.') + resourceId: string + + @sys.description('Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)).') + sharedPublicIpAddressConfiguration: { + @sys.description('Required. Backend ports that virtual machines on this subnet are allowed to expose.') + allowedPorts: { + @sys.description('Required. Backend port of the target virtual machine.') + backendPort: int + + @sys.description('Required. Protocol type of the port.') + transportProtocol: 'Tcp' | 'Udp' + }[] + } + + @sys.description('Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny).') + useInVmCreationPermission: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny).') + usePublicIpAddressPermission: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Optional. The virtual network pool associated with this subnet.') + virtualNetworkPoolName: string? +} diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.json b/avm/res/dev-test-lab/lab/virtualnetwork/main.json index 608e44f7a1..1eab7d68eb 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.json +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.json @@ -6,12 +6,132 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1030641179919111475" + "templateHash": "18309816581107210302" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", diff --git a/avm/res/event-hub/namespace/README.md b/avm/res/event-hub/namespace/README.md index 97cab90521..f26494180b 100644 --- a/avm/res/event-hub/namespace/README.md +++ b/avm/res/event-hub/namespace/README.md @@ -733,7 +733,6 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: true } } ``` @@ -908,9 +907,6 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } - }, - "zoneRedundant": { - "value": true } } } @@ -1840,7 +1836,7 @@ Switch to make the Event Hub Namespace zone redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/event-hub/namespace/main.bicep b/avm/res/event-hub/namespace/main.bicep index eb4a8787d1..3cf195e33a 100644 --- a/avm/res/event-hub/namespace/main.bicep +++ b/avm/res/event-hub/namespace/main.bicep @@ -23,7 +23,7 @@ param skuName string = 'Standard' param skuCapacity int = 1 @description('Optional. Switch to make the Event Hub Namespace zone redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. Switch to enable the Auto Inflate feature of Event Hub. Auto Inflate is not supported in Premium SKU EventHub.') param isAutoInflateEnabled bool = false diff --git a/avm/res/event-hub/namespace/main.json b/avm/res/event-hub/namespace/main.json index dc52e9b105..e2d0f3091e 100644 --- a/avm/res/event-hub/namespace/main.json +++ b/avm/res/event-hub/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6513191517073559510" + "templateHash": "14415344651495261460" }, "name": "Event Hub Namespaces", "description": "This module deploys an Event Hub Namespace.", @@ -522,7 +522,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Switch to make the Event Hub Namespace zone redundant." } diff --git a/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep index cf65bb6f5e..f2a8f96a8f 100644 --- a/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -65,7 +65,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - zoneRedundant: true skuName: 'Standard' skuCapacity: 2 authorizationRules: [ diff --git a/avm/res/event-hub/namespace/version.json b/avm/res/event-hub/namespace/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/event-hub/namespace/version.json +++ b/avm/res/event-hub/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/component/main.bicep b/avm/res/insights/component/main.bicep index 764ddea591..a7a3c60dca 100644 --- a/avm/res/insights/component/main.bicep +++ b/avm/res/insights/component/main.bicep @@ -82,7 +82,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) @@ -90,6 +90,22 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' ) + 'Monitoring Metrics Publisher': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '3913510d-42f4-4e42-8a64-420c390055eb' + ) + 'Application Insights Component Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'ae349356-3a1b-4a5e-921d-050484c6347e' + ) + 'Application Insights Snapshot Debugger': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '08954f03-6346-4c2e-81c0-ec3a5cfae23b' + ) + 'Monitoring Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '749f88d5-cbae-40b8-bcfc-e573ddc772fa' + ) } var formattedRoleAssignments = [ diff --git a/avm/res/insights/component/main.json b/avm/res/insights/component/main.json index 046fb51a09..f7b9e3a933 100644 --- a/avm/res/insights/component/main.json +++ b/avm/res/insights/component/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4240320101610363383" + "templateHash": "707617228684994883" }, "name": "Application Insights", "description": "This component deploys an Application Insights instance.", @@ -361,8 +361,12 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]" } }, "resources": { diff --git a/avm/res/insights/data-collection-rule/README.md b/avm/res/insights/data-collection-rule/README.md index fee76d7ce0..ab796d654b 100644 --- a/avm/res/insights/data-collection-rule/README.md +++ b/avm/res/insights/data-collection-rule/README.md @@ -17,7 +17,7 @@ This module deploys a Data Collection Rule. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Insights/dataCollectionRules` | [2021-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-09-01-preview/dataCollectionRules) | +| `Microsoft.Insights/dataCollectionRules` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-11/dataCollectionRules) | ## Usage examples @@ -27,18 +27,19 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/insights/data-collection-rule:`. -- [Collecting custom text logs with ingestion-time transformation](#example-1-collecting-custom-text-logs-with-ingestion-time-transformation) -- [Collecting custom text logs](#example-2-collecting-custom-text-logs) -- [Collecting IIS logs](#example-3-collecting-iis-logs) -- [Using only defaults](#example-4-using-only-defaults) -- [Collecting Linux-specific information](#example-5-collecting-linux-specific-information) -- [Using large parameter set](#example-6-using-large-parameter-set) -- [WAF-aligned](#example-7-waf-aligned) -- [Collecting Windows-specific information](#example-8-collecting-windows-specific-information) +- [Agent Settings](#example-1-agent-settings) +- [Collecting custom text logs with ingestion-time transformation](#example-2-collecting-custom-text-logs-with-ingestion-time-transformation) +- [Collecting custom text logs](#example-3-collecting-custom-text-logs) +- [Collecting IIS logs](#example-4-collecting-iis-logs) +- [Using only defaults](#example-5-using-only-defaults) +- [Collecting Linux-specific information](#example-6-collecting-linux-specific-information) +- [Using large parameter set](#example-7-using-large-parameter-set) +- [WAF-aligned](#example-8-waf-aligned) +- [Collecting Windows-specific information](#example-9-collecting-windows-specific-information) -### Example 1: _Collecting custom text logs with ingestion-time transformation_ +### Example 1: _Agent Settings_ -This instance deploys the module to setup collection of custom logs and ingestion-time transformation. +This instance deploys the module AMA (Azure Monitor Agent) Settings DCR.
@@ -50,82 +51,158 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableAdvanced_CL' - streams: [ - 'Custom-CustomTableAdvanced_CL' + dataCollectionRuleProperties: { + agentSettings: { + logs: [ + { + name: 'MaxDiskQuotaInMB' + value: '5000' + } ] - transformKql: 'source | extend LogFields = split(RawData, \',\') | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' } - ] - dataSources: { - logFiles: [ + description: 'Agent Settings' + kind: 'AgentSettings' + } + name: 'idcrags001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "dataCollectionRuleProperties": { + "value": { + "agentSettings": { + "logs": [ + { + "name": "MaxDiskQuotaInMB", + "value": "5000" + } + ] + }, + "description": "Agent Settings", + "kind": "AgentSettings" + } + }, + "name": { + "value": "idcrags001" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Collecting custom text logs with ingestion-time transformation_ + +This instance deploys the module to setup collection of custom logs and ingestion-time transformation. + + +

+ +via Bicep module + +```bicep +module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' = { + name: 'dataCollectionRuleDeployment' + params: { + // Required parameters + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsAdvanced\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableAdvanced_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableAdvanced_CL' streams: [ 'Custom-CustomTableAdvanced_CL' ] + transformKql: 'source | extend LogFields = split(RawData, \',\') | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] - } - name: 'idcrcusadv001' - // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \',,,\', for example: \'2023-01-25T20:15:05Z,ERROR,404,Page not found\'' - kind: 'Windows' - location: '' - streamDeclarations: { - 'Custom-CustomTableAdvanced_CL': { - columns: [ - { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'EventTime' - type: 'datetime' - } - { - name: 'EventLevel' - type: 'string' - } - { - name: 'EventCode' - type: 'int' - } + dataSources: { + logFiles: [ { - name: 'Message' - type: 'string' + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] } + ] + } + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \',,,\', for example: \'2023-01-25T20:15:05Z,ERROR,404,Page not found\'' + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + name: '' + workspaceResourceId: '' } ] } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } + name: 'idcrcusadv001' + // Non-required parameters + location: '' tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -148,100 +225,90 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableAdvanced_CL", - "streams": [ - "Custom-CustomTableAdvanced_CL" - ], - "transformKql": "source | extend LogFields = split(RawData, \",\") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsAdvanced\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableAdvanced_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableAdvanced_CL", "streams": [ "Custom-CustomTableAdvanced_CL" - ] + ], + "transformKql": "source | extend LogFields = split(RawData, \",\") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsAdvanced\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableAdvanced_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableAdvanced_CL" + ] + } + ] + }, + "description": "Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \",,,\", for example: \"2023-01-25T20:15:05Z,ERROR,404,Page not found\"", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableAdvanced_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventLevel", + "type": "string" + }, + { + "name": "EventCode", + "type": "int" + }, + { + "name": "Message", + "type": "string" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrcusadv001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \",,,\", for example: \"2023-01-25T20:15:05Z,ERROR,404,Page not found\"" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, - "streamDeclarations": { - "value": { - "Custom-CustomTableAdvanced_CL": { - "columns": [ - { - "name": "TimeGenerated", - "type": "datetime" - }, - { - "name": "EventTime", - "type": "datetime" - }, - { - "name": "EventLevel", - "type": "string" - }, - { - "name": "EventCode", - "type": "int" - }, - { - "name": "Message", - "type": "string" - }, - { - "name": "RawData", - "type": "string" - } - ] - } - } - }, "tags": { "value": { "hidden-title": "This is visible in the resource name", @@ -256,7 +323,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 2: _Collecting custom text logs_ +### Example 3: _Collecting custom text logs_ This instance deploys the module to setup collection of custom logs. @@ -270,66 +337,68 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableBasic_CL' - streams: [ - 'Custom-CustomTableBasic_CL' - ] - transformKql: 'source' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableBasic_CL' streams: [ 'Custom-CustomTableBasic_CL' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] - } - name: 'idcrcusbas001' - // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs without ingestion-time transformation.' - kind: 'Windows' - location: '' - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableBasic_CL' + ] } + ] + } + description: 'Collecting custom text logs without ingestion-time transformation.' + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + name: '' + workspaceResourceId: '' } ] } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } + name: 'idcrcusbas001' + // Non-required parameters + location: '' tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -352,84 +421,74 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableBasic_CL", - "streams": [ - "Custom-CustomTableBasic_CL" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsBasic\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableBasic_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableBasic_CL", "streams": [ "Custom-CustomTableBasic_CL" - ] + ], + "transformKql": "source" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsBasic\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableBasic_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableBasic_CL" + ] + } + ] + }, + "description": "Collecting custom text logs without ingestion-time transformation.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableBasic_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrcusbas001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs without ingestion-time transformation." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, - "streamDeclarations": { - "value": { - "Custom-CustomTableBasic_CL": { - "columns": [ - { - "name": "TimeGenerated", - "type": "datetime" - }, - { - "name": "RawData", - "type": "string" - } - ] - } - } - }, "tags": { "value": { "hidden-title": "This is visible in the resource name", @@ -444,7 +503,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 3: _Collecting IIS logs_ +### Example 4: _Collecting IIS logs_ This instance deploys the module to setup the collection of IIS logs. @@ -458,44 +517,46 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Microsoft-W3CIISLog' - streams: [ - 'Microsoft-W3CIISLog' - ] - transformKql: 'source' - } - ] - dataSources: { - iisLogs: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - logDirectories: [ - 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + destinations: [ + '' ] - name: 'iisLogsDataSource' + outputStream: 'Microsoft-W3CIISLog' streams: [ 'Microsoft-W3CIISLog' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] + dataSources: { + iisLogs: [ + { + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + } + ] + } + description: 'Collecting IIS logs.' + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrcusiis001' // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting IIS logs.' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -519,58 +580,50 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Microsoft-W3CIISLog", - "streams": [ - "Microsoft-W3CIISLog" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "iisLogs": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "logDirectories": [ - "C:\\inetpub\\logs\\LogFiles\\W3SVC1" + "destinations": [ + "" ], - "name": "iisLogsDataSource", + "outputStream": "Microsoft-W3CIISLog", "streams": [ "Microsoft-W3CIISLog" - ] - } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "transformKql": "source" } - ] + ], + "dataSources": { + "iisLogs": [ + { + "logDirectories": [ + "C:\\inetpub\\logs\\LogFiles\\W3SVC1" + ], + "name": "iisLogsDataSource", + "streams": [ + "Microsoft-W3CIISLog" + ] + } + ] + }, + "description": "Collecting IIS logs.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrcusiis001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting IIS logs." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -588,7 +641,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 4: _Using only defaults_ +### Example 5: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -602,47 +655,49 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' } } @@ -661,57 +716,53 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" } @@ -722,7 +773,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 5: _Collecting Linux-specific information_ +### Example 6: _Collecting Linux-specific information_ This instance deploys the module to setup the collection of Linux-specific performance counters and Linux Syslog. @@ -736,157 +787,159 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Syslog' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - 'Logical Disk(*)\\% Free Inodes' - 'Logical Disk(*)\\% Free Space' - 'Logical Disk(*)\\% Used Inodes' - 'Logical Disk(*)\\% Used Space' - 'Logical Disk(*)\\Disk Read Bytes/sec' - 'Logical Disk(*)\\Disk Reads/sec' - 'Logical Disk(*)\\Disk Transfers/sec' - 'Logical Disk(*)\\Disk Write Bytes/sec' - 'Logical Disk(*)\\Disk Writes/sec' - 'Logical Disk(*)\\Free Megabytes' - 'Logical Disk(*)\\Logical Disk Bytes/sec' - 'Memory(*)\\% Available Memory' - 'Memory(*)\\% Available Swap Space' - 'Memory(*)\\% Used Memory' - 'Memory(*)\\% Used Swap Space' - 'Memory(*)\\Available MBytes Memory' - 'Memory(*)\\Available MBytes Swap' - 'Memory(*)\\Page Reads/sec' - 'Memory(*)\\Page Writes/sec' - 'Memory(*)\\Pages/sec' - 'Memory(*)\\Used MBytes Swap Space' - 'Memory(*)\\Used Memory MBytes' - 'Network(*)\\Total Bytes' - 'Network(*)\\Total Bytes Received' - 'Network(*)\\Total Bytes Transmitted' - 'Network(*)\\Total Collisions' - 'Network(*)\\Total Packets Received' - 'Network(*)\\Total Packets Transmitted' - 'Network(*)\\Total Rx Errors' - 'Network(*)\\Total Tx Errors' - 'Processor(*)\\% DPC Time' - 'Processor(*)\\% Idle Time' - 'Processor(*)\\% Interrupt Time' - 'Processor(*)\\% IO Wait Time' - 'Processor(*)\\% Nice Time' - 'Processor(*)\\% Privileged Time' - 'Processor(*)\\% Processor Time' - 'Processor(*)\\% User Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - syslog: [ { - facilityNames: [ - 'auth' - 'authpriv' - ] - logLevels: [ - 'Alert' - 'Critical' - 'Debug' - 'Emergency' - 'Error' - 'Info' - 'Notice' - 'Warning' + destinations: [ + '' ] - name: 'sysLogsDataSource-debugLevel' - streams: [ - 'Microsoft-Syslog' - ] - } - { - facilityNames: [ - 'cron' - 'daemon' - 'kern' - 'local0' - 'mark' - ] - logLevels: [ - 'Alert' - 'Critical' - 'Emergency' - 'Error' - 'Warning' - ] - name: 'sysLogsDataSource-warningLevel' - streams: [ - 'Microsoft-Syslog' - ] - } - { - facilityNames: [ - 'local1' - 'local2' - 'local3' - 'local4' - 'local5' - 'local6' - 'local7' - 'lpr' - 'mail' - 'news' - 'syslog' - ] - logLevels: [ - 'Alert' - 'Critical' - 'Emergency' - 'Error' - ] - name: 'sysLogsDataSource-errLevel' streams: [ 'Microsoft-Syslog' ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\% Used Swap Space' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\Used Memory MBytes' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Collisions' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Processor(*)\\% DPC Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% User Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + syslog: [ + { + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Debug' + 'Emergency' + 'Error' + 'Info' + 'Notice' + 'Warning' + ] + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'cron' + 'daemon' + 'kern' + 'local0' + 'mark' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + 'Warning' + ] + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + ] + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Linux-specific performance counters and Linux Syslog' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Linux' } name: 'idcrlin001' // Non-required parameters - description: 'Collecting Linux-specific performance counters and Linux Syslog' - kind: 'Linux' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -910,169 +963,163 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Syslog" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "Logical Disk(*)\\% Free Inodes", - "Logical Disk(*)\\% Free Space", - "Logical Disk(*)\\% Used Inodes", - "Logical Disk(*)\\% Used Space", - "Logical Disk(*)\\Disk Read Bytes/sec", - "Logical Disk(*)\\Disk Reads/sec", - "Logical Disk(*)\\Disk Transfers/sec", - "Logical Disk(*)\\Disk Write Bytes/sec", - "Logical Disk(*)\\Disk Writes/sec", - "Logical Disk(*)\\Free Megabytes", - "Logical Disk(*)\\Logical Disk Bytes/sec", - "Memory(*)\\% Available Memory", - "Memory(*)\\% Available Swap Space", - "Memory(*)\\% Used Memory", - "Memory(*)\\% Used Swap Space", - "Memory(*)\\Available MBytes Memory", - "Memory(*)\\Available MBytes Swap", - "Memory(*)\\Page Reads/sec", - "Memory(*)\\Page Writes/sec", - "Memory(*)\\Pages/sec", - "Memory(*)\\Used MBytes Swap Space", - "Memory(*)\\Used Memory MBytes", - "Network(*)\\Total Bytes", - "Network(*)\\Total Bytes Received", - "Network(*)\\Total Bytes Transmitted", - "Network(*)\\Total Collisions", - "Network(*)\\Total Packets Received", - "Network(*)\\Total Packets Transmitted", - "Network(*)\\Total Rx Errors", - "Network(*)\\Total Tx Errors", - "Processor(*)\\% DPC Time", - "Processor(*)\\% Idle Time", - "Processor(*)\\% Interrupt Time", - "Processor(*)\\% IO Wait Time", - "Processor(*)\\% Nice Time", - "Processor(*)\\% Privileged Time", - "Processor(*)\\% Processor Time", - "Processor(*)\\% User Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "syslog": [ - { - "facilityNames": [ - "auth", - "authpriv" - ], - "logLevels": [ - "Alert", - "Critical", - "Debug", - "Emergency", - "Error", - "Info", - "Notice", - "Warning" - ], - "name": "sysLogsDataSource-debugLevel", - "streams": [ - "Microsoft-Syslog" - ] }, { - "facilityNames": [ - "cron", - "daemon", - "kern", - "local0", - "mark" - ], - "logLevels": [ - "Alert", - "Critical", - "Emergency", - "Error", - "Warning" + "destinations": [ + "" ], - "name": "sysLogsDataSource-warningLevel", - "streams": [ - "Microsoft-Syslog" - ] - }, - { - "facilityNames": [ - "local1", - "local2", - "local3", - "local4", - "local5", - "local6", - "local7", - "lpr", - "mail", - "news", - "syslog" - ], - "logLevels": [ - "Alert", - "Critical", - "Emergency", - "Error" - ], - "name": "sysLogsDataSource-errLevel", "streams": [ "Microsoft-Syslog" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "Logical Disk(*)\\% Free Inodes", + "Logical Disk(*)\\% Free Space", + "Logical Disk(*)\\% Used Inodes", + "Logical Disk(*)\\% Used Space", + "Logical Disk(*)\\Disk Read Bytes/sec", + "Logical Disk(*)\\Disk Reads/sec", + "Logical Disk(*)\\Disk Transfers/sec", + "Logical Disk(*)\\Disk Write Bytes/sec", + "Logical Disk(*)\\Disk Writes/sec", + "Logical Disk(*)\\Free Megabytes", + "Logical Disk(*)\\Logical Disk Bytes/sec", + "Memory(*)\\% Available Memory", + "Memory(*)\\% Available Swap Space", + "Memory(*)\\% Used Memory", + "Memory(*)\\% Used Swap Space", + "Memory(*)\\Available MBytes Memory", + "Memory(*)\\Available MBytes Swap", + "Memory(*)\\Page Reads/sec", + "Memory(*)\\Page Writes/sec", + "Memory(*)\\Pages/sec", + "Memory(*)\\Used MBytes Swap Space", + "Memory(*)\\Used Memory MBytes", + "Network(*)\\Total Bytes", + "Network(*)\\Total Bytes Received", + "Network(*)\\Total Bytes Transmitted", + "Network(*)\\Total Collisions", + "Network(*)\\Total Packets Received", + "Network(*)\\Total Packets Transmitted", + "Network(*)\\Total Rx Errors", + "Network(*)\\Total Tx Errors", + "Processor(*)\\% DPC Time", + "Processor(*)\\% Idle Time", + "Processor(*)\\% Interrupt Time", + "Processor(*)\\% IO Wait Time", + "Processor(*)\\% Nice Time", + "Processor(*)\\% Privileged Time", + "Processor(*)\\% Processor Time", + "Processor(*)\\% User Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "syslog": [ + { + "facilityNames": [ + "auth", + "authpriv" + ], + "logLevels": [ + "Alert", + "Critical", + "Debug", + "Emergency", + "Error", + "Info", + "Notice", + "Warning" + ], + "name": "sysLogsDataSource-debugLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "cron", + "daemon", + "kern", + "local0", + "mark" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error", + "Warning" + ], + "name": "sysLogsDataSource-warningLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "local1", + "local2", + "local3", + "local4", + "local5", + "local6", + "local7", + "lpr", + "mail", + "news", + "syslog" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error" + ], + "name": "sysLogsDataSource-errLevel", + "streams": [ + "Microsoft-Syslog" + ] + } + ] + }, + "description": "Collecting Linux-specific performance counters and Linux Syslog", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "kind": "Linux" } }, "name": { "value": "idcrlin001" }, // Non-required parameters - "description": { - "value": "Collecting Linux-specific performance counters and Linux Syslog" - }, - "kind": { - "value": "Linux" - }, "location": { "value": "" }, @@ -1090,7 +1137,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 6: _Using large parameter set_ +### Example 7: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -1104,51 +1151,67 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableBasic_CL' - streams: [ - 'Custom-CustomTableBasic_CL' - ] - transformKql: 'source' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableBasic_CL' streams: [ 'Custom-CustomTableBasic_CL' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + dataSources: { + logFiles: [ + { + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableBasic_CL' + ] + } + ] + } + description: 'Collecting custom text logs without ingestion-time transformation.' + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] } - ] + } } name: 'idcrmax001' // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs without ingestion-time transformation.' - kind: 'Windows' location: '' lock: { kind: 'CanNotDelete' @@ -1173,20 +1236,6 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' } ] - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ - { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'RawData' - type: 'string' - } - ] - } - } tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -1209,65 +1258,71 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableBasic_CL", - "streams": [ - "Custom-CustomTableBasic_CL" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsBasic\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableBasic_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableBasic_CL", "streams": [ "Custom-CustomTableBasic_CL" - ] + ], + "transformKql": "source" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsBasic\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableBasic_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableBasic_CL" + ] + } + ] + }, + "description": "Collecting custom text logs without ingestion-time transformation.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableBasic_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrmax001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs without ingestion-time transformation." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1298,22 +1353,6 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 7: _WAF-aligned_ +### Example 8: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -1342,111 +1381,113 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Event' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Page Faults/sec' - '\\Memory\\Pages/sec' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Process(_Total)\\Handle Count' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Context Switches/sec' - '\\System\\Processes' - '\\System\\Processor Queue Length' - '\\System\\System Up Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' + destinations: [ + '' + ] streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Page Faults/sec' + '\\Memory\\Pages/sec' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrwaf001' // Non-required parameters - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -1470,123 +1511,117 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Event" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Page Faults/sec", - "\\Memory\\Pages/sec", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Process(_Total)\\Handle Count", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Context Switches/sec", - "\\System\\Processes", - "\\System\\Processor Queue Length", - "\\System\\System Up Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "windowsEventLogs": [ + }, { - "name": "eventLogsDataSource", + "destinations": [ + "" + ], "streams": [ "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "windowsEventLogs": [ + { + "name": "eventLogsDataSource", + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" + ] + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "description": "Collecting Windows-specific performance counters and Windows Event Logs", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrwaf001" }, // Non-required parameters - "description": { - "value": "Collecting Windows-specific performance counters and Windows Event Logs" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1604,7 +1639,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 8: _Collecting Windows-specific information_ +### Example 9: _Collecting Windows-specific information_ This instance deploys the module to setup the connection of Windows-specific performance counters and Windows Event Logs. @@ -1618,111 +1653,113 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Event' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Page Faults/sec' - '\\Memory\\Pages/sec' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Process(_Total)\\Handle Count' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Context Switches/sec' - '\\System\\Processes' - '\\System\\Processor Queue Length' - '\\System\\System Up Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' + destinations: [ + '' + ] streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Page Faults/sec' + '\\Memory\\Pages/sec' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrwin001' // Non-required parameters - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -1746,123 +1783,117 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Event" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Page Faults/sec", - "\\Memory\\Pages/sec", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Process(_Total)\\Handle Count", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Context Switches/sec", - "\\System\\Processes", - "\\System\\Processor Queue Length", - "\\System\\System Up Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "windowsEventLogs": [ + }, { - "name": "eventLogsDataSource", + "destinations": [ + "" + ], "streams": [ "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "windowsEventLogs": [ + { + "name": "eventLogsDataSource", + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" + ] + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "description": "Collecting Windows-specific performance counters and Windows Event Logs", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrwin001" }, // Non-required parameters - "description": { - "value": "Collecting Windows-specific performance counters and Windows Event Logs" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1887,42 +1918,22 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:,,,", for example: "2023-01-25T20:15:05Z,ERROR,404,Page not found"' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableAdvanced_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source | extend LogFields = split(RawData, ",") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' - outputStream: 'Custom-CustomTableAdvanced_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): ",,,", for example: "2023-01-25T20:15:05Z,ERROR,404,Page not found"' + dataFlows: [ { - name: 'CustomTableAdvanced_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableAdvanced_CL' ] - filePatterns: [ - 'C:\\TestLogsAdvanced\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } - } - ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName + transformKql: 'source | extend LogFields = split(RawData, ",") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' + outputStream: 'Custom-CustomTableAdvanced_CL' } ] - } - streamDeclarations: { - 'Custom-CustomTableAdvanced_CL': { - columns: [ - { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'EventTime' - type: 'datetime' - } + dataSources: { + logFiles: [ { - name: 'EventLevel' - type: 'string' - } - { - name: 'EventCode' - type: 'int' - } - { - name: 'Message' - type: 'string' + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep index cf1e2a9b3c..88cca8c40e 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep @@ -52,63 +52,65 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting custom text logs without ingestion-time transformation.' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableBasic_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Custom-CustomTableBasic_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs without ingestion-time transformation.' + dataFlows: [ { - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableBasic_CL' ] - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } - } - ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName + transformKql: 'source' + outputStream: 'Custom-CustomTableBasic_CL' } ] - } - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableBasic_CL' + ] + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep index fa08fbf64c..017061171a 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep @@ -52,42 +52,44 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting IIS logs.' - dataFlows: [ - { - streams: [ - 'Microsoft-W3CIISLog' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Microsoft-W3CIISLog' - } - ] - dataSources: { - iisLogs: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting IIS logs.' + dataFlows: [ { - name: 'iisLogsDataSource' streams: [ 'Microsoft-W3CIISLog' ] - logDirectories: [ - 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] + transformKql: 'source' + outputStream: 'Microsoft-W3CIISLog' } ] + dataSources: { + iisLogs: [ + { + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep index 8a9070c88e..6c6fd466da 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep @@ -42,45 +42,47 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - counterSpecifiers: [ - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Processes' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Handle Count' - '\\System\\System Up Time' - '\\System\\Context Switches/sec' - '\\System\\Processor Queue Length' + destinations: [ + 'azureMonitorMetrics-default' ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - ] - kind: 'Windows' } } ] diff --git a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep index 7ffc209efb..0371f75541 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep @@ -51,155 +51,157 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Linux-specific performance counters and Linux Syslog' - dataSources: { - performanceCounters: [ - { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 - streams: [ - 'Microsoft-InsightsMetrics' - ] - counterSpecifiers: [ - 'Processor(*)\\% Processor Time' - 'Processor(*)\\% Idle Time' - 'Processor(*)\\% User Time' - 'Processor(*)\\% Nice Time' - 'Processor(*)\\% Privileged Time' - 'Processor(*)\\% IO Wait Time' - 'Processor(*)\\% Interrupt Time' - 'Processor(*)\\% DPC Time' - 'Memory(*)\\Available MBytes Memory' - 'Memory(*)\\% Available Memory' - 'Memory(*)\\Used Memory MBytes' - 'Memory(*)\\% Used Memory' - 'Memory(*)\\Pages/sec' - 'Memory(*)\\Page Reads/sec' - 'Memory(*)\\Page Writes/sec' - 'Memory(*)\\Available MBytes Swap' - 'Memory(*)\\% Available Swap Space' - 'Memory(*)\\Used MBytes Swap Space' - 'Memory(*)\\% Used Swap Space' - 'Logical Disk(*)\\% Free Inodes' - 'Logical Disk(*)\\% Used Inodes' - 'Logical Disk(*)\\Free Megabytes' - 'Logical Disk(*)\\% Free Space' - 'Logical Disk(*)\\% Used Space' - 'Logical Disk(*)\\Logical Disk Bytes/sec' - 'Logical Disk(*)\\Disk Read Bytes/sec' - 'Logical Disk(*)\\Disk Write Bytes/sec' - 'Logical Disk(*)\\Disk Transfers/sec' - 'Logical Disk(*)\\Disk Reads/sec' - 'Logical Disk(*)\\Disk Writes/sec' - 'Network(*)\\Total Bytes Transmitted' - 'Network(*)\\Total Bytes Received' - 'Network(*)\\Total Bytes' - 'Network(*)\\Total Packets Transmitted' - 'Network(*)\\Total Packets Received' - 'Network(*)\\Total Rx Errors' - 'Network(*)\\Total Tx Errors' - 'Network(*)\\Total Collisions' - ] - } - ] - syslog: [ - { - name: 'sysLogsDataSource-debugLevel' - streams: [ - 'Microsoft-Syslog' - ] - facilityNames: [ - 'auth' - 'authpriv' - ] - logLevels: [ - 'Debug' - 'Info' - 'Notice' - 'Warning' - 'Error' - 'Critical' - 'Alert' - 'Emergency' - ] + dataCollectionRuleProperties: { + kind: 'Linux' + description: 'Collecting Linux-specific performance counters and Linux Syslog' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% User Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% DPC Time' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\Used Memory MBytes' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\% Used Swap Space' + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Network(*)\\Total Collisions' + ] + } + ] + syslog: [ + { + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Debug' + 'Info' + 'Notice' + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'cron' + 'daemon' + 'mark' + 'kern' + 'local0' + ] + logLevels: [ + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'sysLogsDataSource-warningLevel' streams: [ - 'Microsoft-Syslog' - ] - facilityNames: [ - 'cron' - 'daemon' - 'mark' - 'kern' - 'local0' + 'Microsoft-InsightsMetrics' ] - logLevels: [ - 'Warning' - 'Error' - 'Critical' - 'Alert' - 'Emergency' + destinations: [ + 'azureMonitorMetrics-default' ] } { - name: 'sysLogsDataSource-errLevel' streams: [ 'Microsoft-Syslog' ] - facilityNames: [ - 'local1' - 'local2' - 'local3' - 'local4' - 'local5' - 'local6' - 'local7' - 'lpr' - 'mail' - 'news' - 'syslog' - ] - logLevels: [ - 'Error' - 'Critical' - 'Alert' - 'Emergency' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Syslog' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Linux' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep index 9cb397571f..27ac14b5c4 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep @@ -53,63 +53,65 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting custom text logs without ingestion-time transformation.' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableBasic_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Custom-CustomTableBasic_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs without ingestion-time transformation.' + dataFlows: [ { - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableBasic_CL' ] - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + transformKql: 'source' + outputStream: 'Custom-CustomTableBasic_CL' } ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableBasic_CL' + ] + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' lock: { kind: 'CanNotDelete' name: 'myCustomLockName' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep index c1b93f95b5..797344b7e1 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep @@ -52,109 +52,111 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - counterSpecifiers: [ - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Processes' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Handle Count' - '\\System\\System Up Time' - '\\System\\Context Switches/sec' - '\\System\\Processor Queue Length' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pages/sec' - '\\Memory\\Page Faults/sec' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' + destinations: [ + 'azureMonitorMetrics-default' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Event' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep index d5e29bfc40..7c58e5ede8 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep @@ -51,109 +51,111 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - counterSpecifiers: [ - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Processes' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Handle Count' - '\\System\\System Up Time' - '\\System\\Context Switches/sec' - '\\System\\Processor Queue Length' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pages/sec' - '\\Memory\\Page Faults/sec' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' + destinations: [ + 'azureMonitorMetrics-default' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Event' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/version.json b/avm/res/insights/data-collection-rule/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/data-collection-rule/version.json +++ b/avm/res/insights/data-collection-rule/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/application-gateway/README.md b/avm/res/network/application-gateway/README.md index f3faed0358..eb63406f99 100644 --- a/avm/res/network/application-gateway/README.md +++ b/avm/res/network/application-gateway/README.md @@ -127,14 +127,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1159,11 +1141,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1317,6 +1301,7 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' + firewallPolicyResourceId: '' frontendIPConfigurations: [ { name: 'private' @@ -1644,20 +1629,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:" }, + "firewallPolicyResourceId": { + "value": "" + }, "frontendIPConfigurations": { "value": [ { @@ -2110,24 +2084,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' = storageAccountResourceId: '' workspaceResourceId: '' } + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'sendingDiagnosticSettingsToSelf' + useThisWorkspace: true + } ] gallerySolutions: [ { @@ -325,7 +334,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - useDeployedWorkspaceForDiagnosticSettings: true useResourcePermissions: true } } @@ -484,6 +492,15 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "name": "customSetting", "storageAccountResourceId": "", "workspaceResourceId": "" + }, + { + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "sendingDiagnosticSettingsToSelf", + "useThisWorkspace": true } ] }, @@ -646,9 +663,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "Role": "DeploymentValidation" } }, - "useDeployedWorkspaceForDiagnosticSettings": { - "value": true - }, "useResourcePermissions": { "value": true } @@ -1725,7 +1739,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = | [`storageInsightsConfigs`](#parameter-storageinsightsconfigs) | array | List of storage accounts to be read by the workspace. | | [`tables`](#parameter-tables) | array | LAW custom tables to be deployed. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`useDeployedWorkspaceForDiagnosticSettings`](#parameter-usedeployedworkspacefordiagnosticsettings) | bool | Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. | | [`useResourcePermissions`](#parameter-useresourcepermissions) | bool | Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. | ### Parameter: `name` @@ -1794,6 +1807,7 @@ The diagnostic settings of the service. | [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | | [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | | [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`useThisWorkspace`](#parameter-diagnosticsettingsusethisworkspace) | bool | Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored. | | [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | ### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` @@ -1914,6 +1928,13 @@ Resource ID of the diagnostic storage account. For security reasons, it is recom - Required: No - Type: string +### Parameter: `diagnosticSettings.useThisWorkspace` + +Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored. + +- Required: No +- Type: bool + ### Parameter: `diagnosticSettings.workspaceResourceId` Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. @@ -2212,14 +2233,6 @@ Tags of the resource. - Required: No - Type: object -### Parameter: `useDeployedWorkspaceForDiagnosticSettings` - -Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. - -- Required: No -- Type: bool -- Default: `False` - ### Parameter: `useResourcePermissions` Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. diff --git a/avm/res/operational-insights/workspace/main.bicep b/avm/res/operational-insights/workspace/main.bicep index a9f1e5c922..4c6bd5e5a1 100644 --- a/avm/res/operational-insights/workspace/main.bicep +++ b/avm/res/operational-insights/workspace/main.bicep @@ -82,9 +82,6 @@ param useResourcePermissions bool = false @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingType -@description('Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings.') -param useDeployedWorkspaceForDiagnosticSettings bool = false - @description('Optional. Indicates whether customer managed storage is mandatory for query management.') param forceCmkForQuery bool = true @@ -212,7 +209,7 @@ resource logAnalyticsWorkspace_diagnosticSettings 'Microsoft.Insights/diagnostic name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' properties: { storageAccountId: diagnosticSetting.?storageAccountResourceId - workspaceId: useDeployedWorkspaceForDiagnosticSettings + workspaceId: (diagnosticSetting.?useThisWorkspace ?? false) ? logAnalyticsWorkspace.id : diagnosticSetting.?workspaceResourceId eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId @@ -482,6 +479,9 @@ type diagnosticSettingType = { @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics' | null)? + @description('Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored.') + useThisWorkspace: bool? + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') workspaceResourceId: string? diff --git a/avm/res/operational-insights/workspace/main.json b/avm/res/operational-insights/workspace/main.json index 1e54043d68..ccbd83e3c1 100644 --- a/avm/res/operational-insights/workspace/main.json +++ b/avm/res/operational-insights/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11421725796221742541" + "templateHash": "11994891550110156380" }, "name": "Log Analytics Workspaces", "description": "This module deploys a Log Analytics Workspace.", @@ -215,6 +215,13 @@ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." } }, + "useThisWorkspace": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored." + } + }, "workspaceResourceId": { "type": "string", "nullable": true, @@ -409,13 +416,6 @@ "description": "Optional. The diagnostic settings of the service." } }, - "useDeployedWorkspaceForDiagnosticSettings": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings." - } - }, "forceCmkForQuery": { "type": "bool", "defaultValue": true, @@ -551,7 +551,7 @@ } ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[if(parameters('useDeployedWorkspaceForDiagnosticSettings'), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId'))]", + "workspaceId": "[if(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'useThisWorkspace'), false()), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId'))]", "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", diff --git a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep index e0025415db..94d627e6a7 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep @@ -174,8 +174,16 @@ module testDeployment '../../../main.bicep' = [ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } + { + name: 'sendingDiagnosticSettingsToSelf' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + useThisWorkspace: true + } ] - useDeployedWorkspaceForDiagnosticSettings: true gallerySolutions: [ { name: 'AzureAutomation' diff --git a/avm/res/operational-insights/workspace/version.json b/avm/res/operational-insights/workspace/version.json index a8eda31021..e42c3d9e5f 100644 --- a/avm/res/operational-insights/workspace/version.json +++ b/avm/res/operational-insights/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/sql/server/database/README.md b/avm/res/sql/server/database/README.md index 48abd9e22f..304baed8c1 100644 --- a/avm/res/sql/server/database/README.md +++ b/avm/res/sql/server/database/README.md @@ -484,7 +484,7 @@ Whether or not this database is zone redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/sql/server/database/main.bicep b/avm/res/sql/server/database/main.bicep index ff985b8eb6..f26259ab84 100644 --- a/avm/res/sql/server/database/main.bicep +++ b/avm/res/sql/server/database/main.bicep @@ -41,7 +41,7 @@ param maxSizeBytes int = 34359738368 param sampleName string = '' @description('Optional. Whether or not this database is zone redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. The license type to apply for this database.') param licenseType string = '' diff --git a/avm/res/sql/server/database/main.json b/avm/res/sql/server/database/main.json index 6a3ecbe9b8..a28b55efe8 100644 --- a/avm/res/sql/server/database/main.json +++ b/avm/res/sql/server/database/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8343448660522432164" + "templateHash": "6019999815954957727" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -217,7 +217,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this database is zone redundant." } diff --git a/avm/res/sql/server/elastic-pool/README.md b/avm/res/sql/server/elastic-pool/README.md index fb55fe4ad4..d787a2e320 100644 --- a/avm/res/sql/server/elastic-pool/README.md +++ b/avm/res/sql/server/elastic-pool/README.md @@ -168,7 +168,7 @@ Whether or not this elastic pool is zone redundant, which means the replicas of - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/sql/server/elastic-pool/main.bicep b/avm/res/sql/server/elastic-pool/main.bicep index 2e884fbc49..606aded8d8 100644 --- a/avm/res/sql/server/elastic-pool/main.bicep +++ b/avm/res/sql/server/elastic-pool/main.bicep @@ -49,7 +49,7 @@ param databaseMaxCapacity int = 2 param databaseMinCapacity int = 0 @description('Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones.') -param zoneRedundant bool = false +param zoneRedundant bool = true resource server 'Microsoft.Sql/servers@2023-08-01-preview' existing = { name: serverName diff --git a/avm/res/sql/server/elastic-pool/main.json b/avm/res/sql/server/elastic-pool/main.json index bc9ef67970..018727aa52 100644 --- a/avm/res/sql/server/elastic-pool/main.json +++ b/avm/res/sql/server/elastic-pool/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17663884310941548142" + "templateHash": "18037703368269722870" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -115,7 +115,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." } diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 100d49f9d9..50bda35805 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -263,7 +263,7 @@ module server_databases 'database/main.bicep' = [ : '' sampleName: contains(database, 'sampleName') ? database.sampleName : '' tags: database.?tags ?? tags - zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : false + zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : true elasticPoolId: contains(database, 'elasticPoolId') ? database.elasticPoolId : '' backupShortTermRetentionPolicy: contains(database, 'backupShortTermRetentionPolicy') ? database.backupShortTermRetentionPolicy @@ -305,7 +305,7 @@ module server_elasticPools 'elastic-pool/main.bicep' = [ skuCapacity: contains(elasticPool, 'skuCapacity') ? elasticPool.skuCapacity : 2 skuName: contains(elasticPool, 'skuName') ? elasticPool.skuName : 'GP_Gen5' skuTier: contains(elasticPool, 'skuTier') ? elasticPool.skuTier : 'GeneralPurpose' - zoneRedundant: contains(elasticPool, 'zoneRedundant') ? elasticPool.zoneRedundant : false + zoneRedundant: contains(elasticPool, 'zoneRedundant') ? elasticPool.zoneRedundant : true location: location tags: elasticPool.?tags ?? tags } diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index 78cff13981..9bf2e9b5c3 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18130756437591455522" + "templateHash": "8727418248832787909" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -740,7 +740,7 @@ "tags": { "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "zoneRedundant": "[if(contains(parameters('databases')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('databases')[copyIndex()].zoneRedundant), createObject('value', false()))]", + "zoneRedundant": "[if(contains(parameters('databases')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('databases')[copyIndex()].zoneRedundant), createObject('value', true()))]", "elasticPoolId": "[if(contains(parameters('databases')[copyIndex()], 'elasticPoolId'), createObject('value', parameters('databases')[copyIndex()].elasticPoolId), createObject('value', ''))]", "backupShortTermRetentionPolicy": "[if(contains(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicy'), createObject('value', parameters('databases')[copyIndex()].backupShortTermRetentionPolicy), createObject('value', createObject()))]", "backupLongTermRetentionPolicy": "[if(contains(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicy'), createObject('value', parameters('databases')[copyIndex()].backupLongTermRetentionPolicy), createObject('value', createObject()))]", @@ -758,7 +758,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8343448660522432164" + "templateHash": "6019999815954957727" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -969,7 +969,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this database is zone redundant." } @@ -1500,7 +1500,7 @@ "skuCapacity": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuCapacity'), createObject('value', parameters('elasticPools')[copyIndex()].skuCapacity), createObject('value', 2))]", "skuName": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuName'), createObject('value', parameters('elasticPools')[copyIndex()].skuName), createObject('value', 'GP_Gen5'))]", "skuTier": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuTier'), createObject('value', parameters('elasticPools')[copyIndex()].skuTier), createObject('value', 'GeneralPurpose'))]", - "zoneRedundant": "[if(contains(parameters('elasticPools')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('elasticPools')[copyIndex()].zoneRedundant), createObject('value', false()))]", + "zoneRedundant": "[if(contains(parameters('elasticPools')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('elasticPools')[copyIndex()].zoneRedundant), createObject('value', true()))]", "location": { "value": "[parameters('location')]" }, @@ -1516,7 +1516,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17663884310941548142" + "templateHash": "18037703368269722870" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -1625,7 +1625,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." } diff --git a/avm/res/sql/server/version.json b/avm/res/sql/server/version.json index a8eda31021..9ed3662aba 100644 --- a/avm/res/sql/server/version.json +++ b/avm/res/sql/server/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index 87b8afe096..caff6c5ab8 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -28,6 +28,7 @@ This module deploys a Synapse Workspace. | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Synapse/workspaces` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces) | | `Microsoft.Synapse/workspaces/administrators` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/administrators) | +| `Microsoft.Synapse/workspaces/firewallRules` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/firewallRules) | | `Microsoft.Synapse/workspaces/integrationRuntimes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/integrationRuntimes) | | `Microsoft.Synapse/workspaces/keys` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) | @@ -42,9 +43,10 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using encryption with Customer-Managed-Key](#example-2-using-encryption-with-customer-managed-key) - [Using encryption with Customer-Managed-Key](#example-3-using-encryption-with-customer-managed-key) -- [Using managed Vnet](#example-4-using-managed-vnet) -- [Using large parameter set](#example-5-using-large-parameter-set) -- [WAF-aligned](#example-6-waf-aligned) +- [Using firewall rules](#example-4-using-firewall-rules) +- [Using managed Vnet](#example-5-using-managed-vnet) +- [Using large parameter set](#example-6-using-large-parameter-set) +- [WAF-aligned](#example-7-waf-aligned) ### Example 1: _Using only defaults_ @@ -252,7 +254,93 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 4: _Using managed Vnet_ +### Example 4: _Using firewall rules_ + +This instance deploys the module with the configuration of firewall rules. + + +

+ +via Bicep module + +```bicep +module workspace 'br/public:avm/res/synapse/workspace:' = { + name: 'workspaceDeployment' + params: { + // Required parameters + defaultDataLakeStorageAccountResourceId: '' + defaultDataLakeStorageFilesystem: '' + name: 'swfwr001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + firewallRules: [ + { + endIpAddress: '87.14.134.20' + name: 'fwrule01' + startIpAddress: '87.14.134.20' + } + { + endIpAddress: '87.14.134.22' + name: 'fwrule02' + startIpAddress: '87.14.134.21' + } + ] + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "defaultDataLakeStorageAccountResourceId": { + "value": "" + }, + "defaultDataLakeStorageFilesystem": { + "value": "" + }, + "name": { + "value": "swfwr001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "firewallRules": { + "value": [ + { + "endIpAddress": "87.14.134.20", + "name": "fwrule01", + "startIpAddress": "87.14.134.20" + }, + { + "endIpAddress": "87.14.134.22", + "name": "fwrule02", + "startIpAddress": "87.14.134.21" + } + ] + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Using managed Vnet_ This instance deploys the module using a managed Vnet. @@ -328,7 +416,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 5: _Using large parameter set_ +### Example 6: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -602,7 +690,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 6: _WAF-aligned_ +### Example 7: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -783,6 +871,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`encryptionActivateWorkspace`](#parameter-encryptionactivateworkspace) | bool | Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace. | +| [`firewallRules`](#parameter-firewallrules) | array | List of firewall rules to be created in the workspace. | | [`initialWorkspaceAdminObjectID`](#parameter-initialworkspaceadminobjectid) | string | AAD object ID of initial workspace admin. | | [`integrationRuntimes`](#parameter-integrationruntimes) | array | The Integration Runtimes to create. | | [`linkedAccessCheckOnTargetResource`](#parameter-linkedaccesscheckontargetresource) | bool | Linked Access Check On Target Resource. | @@ -1078,6 +1167,42 @@ Activate workspace by adding the system managed identity in the KeyVault contain - Type: bool - Default: `False` +### Parameter: `firewallRules` + +List of firewall rules to be created in the workspace. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endIpAddress`](#parameter-firewallrulesendipaddress) | string | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. | +| [`name`](#parameter-firewallrulesname) | string | The name of the firewall rule. | +| [`startIpAddress`](#parameter-firewallrulesstartipaddress) | string | The start IP address of the firewall rule. Must be IPv4 format. | + +### Parameter: `firewallRules.endIpAddress` + +The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. + +- Required: Yes +- Type: string + +### Parameter: `firewallRules.name` + +The name of the firewall rule. + +- Required: Yes +- Type: string + +### Parameter: `firewallRules.startIpAddress` + +The start IP address of the firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + ### Parameter: `initialWorkspaceAdminObjectID` AAD object ID of initial workspace admin. diff --git a/avm/res/synapse/workspace/firewall-rules/README.md b/avm/res/synapse/workspace/firewall-rules/README.md new file mode 100644 index 0000000000..0816f90472 --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/README.md @@ -0,0 +1,78 @@ +# Synapse Workspaces Firewall Rules `[Microsoft.Synapse/workspaces/firewallRules]` + +This module deploys Synapse Workspaces Firewall Rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Synapse/workspaces/firewallRules` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/firewallRules) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endIpAddress`](#parameter-endipaddress) | string | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. | +| [`name`](#parameter-name) | string | The name of the firewall rule. | +| [`startIpAddress`](#parameter-startipaddress) | string | The start IP address of the firewall rule. Must be IPv4 format. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`workspaceName`](#parameter-workspacename) | string | The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. | + +### Parameter: `endIpAddress` + +The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. + +- Required: Yes +- Type: string + +### Parameter: `name` + +The name of the firewall rule. + +- Required: Yes +- Type: string + +### Parameter: `startIpAddress` + +The start IP address of the firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + +### Parameter: `workspaceName` + +The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed firewall rule. | +| `resourceGroupName` | string | The resource group of the deployed firewall rule. | +| `resourceId` | string | The resource ID of the deployed firewall rule. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/synapse/workspace/firewall-rules/main.bicep b/avm/res/synapse/workspace/firewall-rules/main.bicep new file mode 100644 index 0000000000..a3e12b4eca --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/main.bicep @@ -0,0 +1,37 @@ +metadata name = 'Synapse Workspaces Firewall Rules' +metadata description = 'This module deploys Synapse Workspaces Firewall Rules.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Required. The name of the firewall rule.') +param name string + +@description('Required. The start IP address of the firewall rule. Must be IPv4 format.') +param startIpAddress string + +@description('Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress.') +param endIpAddress string + +resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { + name: workspaceName +} + +resource firewallRule 'Microsoft.Synapse/workspaces/firewallRules@2021-06-01' = { + name: name + parent: workspace + properties: { + startIpAddress: startIpAddress + endIpAddress: endIpAddress + } +} + +@description('The name of the deployed firewall rule.') +output name string = firewallRule.name + +@description('The resource ID of the deployed firewall rule.') +output resourceId string = firewallRule.id + +@description('The resource group of the deployed firewall rule.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/synapse/workspace/firewall-rules/main.json b/avm/res/synapse/workspace/firewall-rules/main.json new file mode 100644 index 0000000000..ce7fd0baaa --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/main.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6729418057172891787" + }, + "name": "Synapse Workspaces Firewall Rules", + "description": "This module deploys Synapse Workspaces Firewall Rules.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/firewallRules", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed firewall rule." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/firewallRules', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed firewall rule." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index d83c44ebbe..5c3a0f2768 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -66,6 +66,9 @@ param preventDataExfiltration bool = false @description('Optional. Enable or Disable public network access to workspace.') param publicNetworkAccess string = 'Enabled' +@description('Optional. List of firewall rules to be created in the workspace.') +param firewallRules firewallRuleType[]? + @description('Optional. Purview Resource ID.') param purviewResourceID string = '' @@ -329,6 +332,19 @@ resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 } ] +// Firewall Rules +module workspace_firewallRules 'firewall-rules/main.bicep' = [ + for (rule, index) in (firewallRules ?? []): { + name: '${uniqueString(deployment().name, location)}-workspace-FirewallRule-${index}' + params: { + name: rule.name + endIpAddress: rule.endIpAddress + startIpAddress: rule.startIpAddress + workspaceName: workspace.name + } + } +] + // Endpoints module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { @@ -606,3 +622,14 @@ type adminType = { @secure() tenantId: string? }? + +type firewallRuleType = { + @description('Required. The name of the firewall rule.') + name: string + + @description('Required. The start IP address of the firewall rule. Must be IPv4 format.') + startIpAddress: string + + @description('Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress.') + endIpAddress: string +} diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 4ff8283f79..b38d4a74e4 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16478885411774242991" + "templateHash": "12241893102141299283" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -472,6 +472,29 @@ } }, "nullable": true + }, + "firewallRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + } } }, "parameters": { @@ -609,6 +632,16 @@ "description": "Optional. Enable or Disable public network access to workspace." } }, + "firewallRules": { + "type": "array", + "items": { + "$ref": "#/definitions/firewallRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of firewall rules to be created in the workspace." + } + }, "purviewResourceID": { "type": "string", "defaultValue": "", @@ -1245,6 +1278,112 @@ "workspace" ] }, + "workspace_firewallRules": { + "copy": { + "name": "workspace_firewallRules", + "count": "[length(coalesce(parameters('firewallRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspace-FirewallRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].name]" + }, + "endIpAddress": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].endIpAddress]" + }, + "startIpAddress": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].startIpAddress]" + }, + "workspaceName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6729418057172891787" + }, + "name": "Synapse Workspaces Firewall Rules", + "description": "This module deploys Synapse Workspaces Firewall Rules.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/firewallRules", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed firewall rule." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/firewallRules', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed firewall rule." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "workspace" + ] + }, "workspace_privateEndpoints": { "copy": { "name": "workspace_privateEndpoints", diff --git a/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep new file mode 100644 index 0000000000..11a476b1e7 --- /dev/null +++ b/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + isHnsEnabled: true + } + + resource blobService 'blobServices@2022-09-01' = { + name: 'default' + + resource container 'containers@2022-09-01' = { + name: 'synapsews' + } + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The name of the created container.') +output storageContainerName string = storageAccount::blobService::container.name diff --git a/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep b/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep new file mode 100644 index 0000000000..4b5ea8d460 --- /dev/null +++ b/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +metadata name = 'Using firewall rules' +metadata description = 'This instance deploys the module with the configuration of firewall rules.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-synapse.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'swfwr' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + storageAccountName: 'dep${namePrefix}sa${serviceShort}01' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + defaultDataLakeStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + defaultDataLakeStorageFilesystem: nestedDependencies.outputs.storageContainerName + sqlAdministratorLogin: 'synwsadmin' + firewallRules: [ + { + name: 'fwrule01' + endIpAddress: '87.14.134.20' + startIpAddress: '87.14.134.20' + } + { + name: 'fwrule02' + endIpAddress: '87.14.134.22' + startIpAddress: '87.14.134.21' + } + ] + } + } +]