From 341743c0d237743210eb843d7e9467cf41ba9080 Mon Sep 17 00:00:00 2001 From: Peter Budai Date: Sun, 13 Oct 2024 11:40:08 +0200 Subject: [PATCH] feat: Update SQL Server API - `avm/res/sql/server` (#3325) * Add support for enabling IPv6 in SQL server * Update allowed values for publicNetworkAccess * Refactor parameter assignments to fix linter warnings ## Description ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.sql.server](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/sql/server/README.md | 21 +- avm/res/sql/server/database/README.md | 1 - avm/res/sql/server/database/main.bicep | 26 +- avm/res/sql/server/database/main.json | 30 +- avm/res/sql/server/elastic-pool/README.md | 1 - avm/res/sql/server/elastic-pool/main.bicep | 2 +- avm/res/sql/server/elastic-pool/main.json | 4 +- avm/res/sql/server/main.bicep | 155 ++++----- avm/res/sql/server/main.json | 318 ++++++++++++------ .../sql/server/tests/e2e/max/main.test.bicep | 21 +- .../tests/e2e/waf-aligned/main.test.bicep | 22 +- avm/res/sql/server/version.json | 2 +- 12 files changed, 359 insertions(+), 244 deletions(-) diff --git a/avm/res/sql/server/README.md b/avm/res/sql/server/README.md index 92be8ea923..1869610a60 100644 --- a/avm/res/sql/server/README.md +++ b/avm/res/sql/server/README.md @@ -471,7 +471,6 @@ module server 'br/public:avm/res/sql/server:' = { ] elasticPools: [ { - maintenanceConfigurationId: '' name: 'sqlsmax-ep-001' skuCapacity: 10 skuName: 'GP_Gen5' @@ -641,7 +640,6 @@ module server 'br/public:avm/res/sql/server:' = { "elasticPools": { "value": [ { - "maintenanceConfigurationId": "", "name": "sqlsmax-ep-001", "skuCapacity": 10, "skuName": "GP_Gen5", @@ -829,7 +827,6 @@ param databases = [ ] param elasticPools = [ { - maintenanceConfigurationId: '' name: 'sqlsmax-ep-001' skuCapacity: 10 skuName: 'GP_Gen5' @@ -1680,6 +1677,7 @@ param vulnerabilityAssessmentsObj = { | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`encryptionProtectorObj`](#parameter-encryptionprotectorobj) | object | The encryption protection configuration. | | [`firewallRules`](#parameter-firewallrules) | array | The firewall rules to create in the server. | +| [`isIPv6Enabled`](#parameter-isipv6enabled) | string | Whether or not to enable IPv6 support for this server. | | [`keys`](#parameter-keys) | array | The keys to configure. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | @@ -1878,6 +1876,21 @@ The firewall rules to create in the server. - Type: array - Default: `[]` +### Parameter: `isIPv6Enabled` + +Whether or not to enable IPv6 support for this server. + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + ### Parameter: `keys` The keys to configure. @@ -1971,6 +1984,7 @@ Minimal TLS version allowed. '1.0' '1.1' '1.2' + '1.3' ] ``` @@ -2393,6 +2407,7 @@ Whether or not public network access is allowed for this resource. For security '' 'Disabled' 'Enabled' + 'SecuredByPerimeter' ] ``` diff --git a/avm/res/sql/server/database/README.md b/avm/res/sql/server/database/README.md index e2268849ef..1abb8e303b 100644 --- a/avm/res/sql/server/database/README.md +++ b/avm/res/sql/server/database/README.md @@ -324,7 +324,6 @@ Maintenance configuration ID assigned to the database. This configuration define - Required: No - Type: string -- Default: `''` ### Parameter: `maxSizeBytes` diff --git a/avm/res/sql/server/database/main.bicep b/avm/res/sql/server/database/main.bicep index f26259ab84..8919f4c720 100644 --- a/avm/res/sql/server/database/main.bicep +++ b/avm/res/sql/server/database/main.bicep @@ -112,7 +112,7 @@ param requestedBackupStorageRedundancy string = '' param isLedgerOn bool = false @description('Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur.') -param maintenanceConfigurationId string = '' +param maintenanceConfigurationId string? @description('Optional. The short term backup retention policy to create for the database.') param backupShortTermRetentionPolicy object = {} @@ -164,7 +164,7 @@ resource database 'Microsoft.Sql/servers/databases@2023-08-01-preview' = { highAvailabilityReplicaCount: highAvailabilityReplicaCount requestedBackupStorageRedundancy: any(requestedBackupStorageRedundancy) isLedgerOn: isLedgerOn - maintenanceConfigurationId: !empty(maintenanceConfigurationId) ? maintenanceConfigurationId : null + maintenanceConfigurationId: maintenanceConfigurationId elasticPoolId: elasticPoolId createMode: createMode sourceDatabaseId: !empty(sourceDatabaseResourceId) ? sourceDatabaseResourceId : null @@ -211,12 +211,8 @@ module database_backupShortTermRetentionPolicy 'backup-short-term-retention-poli params: { serverName: serverName databaseName: database.name - diffBackupIntervalInHours: contains(backupShortTermRetentionPolicy, 'diffBackupIntervalInHours') - ? backupShortTermRetentionPolicy.diffBackupIntervalInHours - : 24 - retentionDays: contains(backupShortTermRetentionPolicy, 'retentionDays') - ? backupShortTermRetentionPolicy.retentionDays - : 7 + diffBackupIntervalInHours: backupShortTermRetentionPolicy.?diffBackupIntervalInHours ?? 24 + retentionDays: backupShortTermRetentionPolicy.?retentionDays ?? 7 } } @@ -225,16 +221,10 @@ module database_backupLongTermRetentionPolicy 'backup-long-term-retention-policy params: { serverName: serverName databaseName: database.name - weeklyRetention: contains(backupLongTermRetentionPolicy, 'weeklyRetention') - ? backupLongTermRetentionPolicy.weeklyRetention - : '' - monthlyRetention: contains(backupLongTermRetentionPolicy, 'monthlyRetention') - ? backupLongTermRetentionPolicy.monthlyRetention - : '' - yearlyRetention: contains(backupLongTermRetentionPolicy, 'yearlyRetention') - ? backupLongTermRetentionPolicy.yearlyRetention - : '' - weekOfYear: contains(backupLongTermRetentionPolicy, 'weekOfYear') ? backupLongTermRetentionPolicy.weekOfYear : 1 + weeklyRetention: backupLongTermRetentionPolicy.?weeklyRetention ?? '' + monthlyRetention: backupLongTermRetentionPolicy.?monthlyRetention ?? '' + yearlyRetention: backupLongTermRetentionPolicy.?yearlyRetention ?? '' + weekOfYear: backupLongTermRetentionPolicy.?weekOfYear ?? 1 } } diff --git a/avm/res/sql/server/database/main.json b/avm/res/sql/server/database/main.json index a28b55efe8..4be7b1af7f 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": "6019999815954957727" + "templateHash": "18021918128213514276" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -355,7 +355,7 @@ }, "maintenanceConfigurationId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur." } @@ -404,7 +404,7 @@ "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", "isLedgerOn": "[parameters('isLedgerOn')]", - "maintenanceConfigurationId": "[if(not(empty(parameters('maintenanceConfigurationId'))), parameters('maintenanceConfigurationId'), null())]", + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", "elasticPoolId": "[parameters('elasticPoolId')]", "createMode": "[parameters('createMode')]", "sourceDatabaseId": "[if(not(empty(parameters('sourceDatabaseResourceId'))), parameters('sourceDatabaseResourceId'), null())]", @@ -474,8 +474,12 @@ "databaseName": { "value": "[parameters('name')]" }, - "diffBackupIntervalInHours": "[if(contains(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours'), createObject('value', parameters('backupShortTermRetentionPolicy').diffBackupIntervalInHours), createObject('value', 24))]", - "retentionDays": "[if(contains(parameters('backupShortTermRetentionPolicy'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPolicy').retentionDays), createObject('value', 7))]" + "diffBackupIntervalInHours": { + "value": "[coalesce(tryGet(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours'), 24)]" + }, + "retentionDays": { + "value": "[coalesce(tryGet(parameters('backupShortTermRetentionPolicy'), 'retentionDays'), 7)]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -574,10 +578,18 @@ "databaseName": { "value": "[parameters('name')]" }, - "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').weeklyRetention), createObject('value', ''))]", - "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').monthlyRetention), createObject('value', ''))]", - "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').yearlyRetention), createObject('value', ''))]", - "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPolicy').weekOfYear), createObject('value', 1))]" + "weeklyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention'), '')]" + }, + "monthlyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention'), '')]" + }, + "yearlyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention'), '')]" + }, + "weekOfYear": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'weekOfYear'), 1)]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", diff --git a/avm/res/sql/server/elastic-pool/README.md b/avm/res/sql/server/elastic-pool/README.md index 69b5e3ec50..ef598e5fa3 100644 --- a/avm/res/sql/server/elastic-pool/README.md +++ b/avm/res/sql/server/elastic-pool/README.md @@ -112,7 +112,6 @@ Maintenance configuration resource ID assigned to the elastic pool. This configu - Required: No - Type: string -- Default: `''` ### Parameter: `maxSizeBytes` diff --git a/avm/res/sql/server/elastic-pool/main.bicep b/avm/res/sql/server/elastic-pool/main.bicep index 606aded8d8..df5d5510ea 100644 --- a/avm/res/sql/server/elastic-pool/main.bicep +++ b/avm/res/sql/server/elastic-pool/main.bicep @@ -34,7 +34,7 @@ param highAvailabilityReplicaCount int? param licenseType string = 'LicenseIncluded' @description('Optional. Maintenance configuration resource ID assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur.') -param maintenanceConfigurationId string = '' +param maintenanceConfigurationId string? @description('Optional. The storage limit for the database elastic pool in bytes.') param maxSizeBytes int = 34359738368 diff --git a/avm/res/sql/server/elastic-pool/main.json b/avm/res/sql/server/elastic-pool/main.json index 018727aa52..5acc2b0818 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": "18037703368269722870" + "templateHash": "17774091526328280898" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -80,7 +80,7 @@ }, "maintenanceConfigurationId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Maintenance configuration resource ID assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." } diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 6a3921b4eb..3919e4655a 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -58,10 +58,18 @@ param administrators object = {} '1.0' '1.1' '1.2' + '1.3' ]) @description('Optional. Minimal TLS version allowed.') param minimalTlsVersion string = '1.2' +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. Whether or not to enable IPv6 support for this server.') +param isIPv6Enabled string = 'Disabled' + @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType @@ -70,6 +78,7 @@ param privateEndpoints privateEndpointType '' 'Enabled' 'Disabled' + 'SecuredByPerimeter' ]) param publicNetworkAccess string = '' @@ -202,9 +211,10 @@ resource server 'Microsoft.Sql/servers@2023-08-01-preview' = { minimalTlsVersion: minimalTlsVersion primaryUserAssignedIdentityId: !empty(primaryUserAssignedIdentityId) ? primaryUserAssignedIdentityId : null publicNetworkAccess: !empty(publicNetworkAccess) - ? any(publicNetworkAccess) + ? publicNetworkAccess : (!empty(privateEndpoints) && empty(firewallRules) && empty(virtualNetworkRules) ? 'Disabled' : null) restrictOutboundNetworkAccess: !empty(restrictOutboundNetworkAccess) ? restrictOutboundNetworkAccess : null + isIPv6Enabled: isIPv6Enabled } } @@ -241,48 +251,34 @@ module server_databases 'database/main.bicep' = [ params: { name: database.name serverName: server.name - skuTier: contains(database, 'skuTier') ? database.skuTier : 'GeneralPurpose' - skuName: contains(database, 'skuName') ? database.skuName : 'GP_Gen5_2' + skuTier: database.?skuTier ?? 'GeneralPurpose' + skuName: database.?skuName ?? 'GP_Gen5_2' skuCapacity: database.?skuCapacity - skuFamily: contains(database, 'skuFamily') ? database.skuFamily : '' - skuSize: contains(database, 'skuSize') ? database.skuSize : '' - collation: contains(database, 'collation') ? database.collation : 'SQL_Latin1_General_CP1_CI_AS' - maxSizeBytes: contains(database, 'maxSizeBytes') ? database.maxSizeBytes : 34359738368 - autoPauseDelay: contains(database, 'autoPauseDelay') ? database.autoPauseDelay : 0 + skuFamily: database.?skuFamily ?? '' + skuSize: database.?skuSize ?? '' + collation: database.?collation ?? 'SQL_Latin1_General_CP1_CI_AS' + maxSizeBytes: database.?maxSizeBytes ?? 34359738368 + autoPauseDelay: database.?autoPauseDelay ?? 0 diagnosticSettings: database.?diagnosticSettings - isLedgerOn: contains(database, 'isLedgerOn') ? database.isLedgerOn : false + isLedgerOn: database.?isLedgerOn ?? false location: location - licenseType: contains(database, 'licenseType') ? database.licenseType : '' - maintenanceConfigurationId: contains(database, 'maintenanceConfigurationId') - ? database.maintenanceConfigurationId - : '' - minCapacity: contains(database, 'minCapacity') ? database.minCapacity : '' - highAvailabilityReplicaCount: contains(database, 'highAvailabilityReplicaCount') - ? database.highAvailabilityReplicaCount - : 0 - readScale: contains(database, 'readScale') ? database.readScale : 'Disabled' - requestedBackupStorageRedundancy: contains(database, 'requestedBackupStorageRedundancy') - ? database.requestedBackupStorageRedundancy - : '' - sampleName: contains(database, 'sampleName') ? database.sampleName : '' + licenseType: database.?licenseType ?? '' + maintenanceConfigurationId: database.?maintenanceConfigurationId + minCapacity: database.?minCapacity ?? '' + highAvailabilityReplicaCount: database.?highAvailabilityReplicaCount ?? 0 + readScale: database.?readScale ?? 'Disabled' + requestedBackupStorageRedundancy: database.?requestedBackupStorageRedundancy ?? '' + sampleName: database.?sampleName ?? '' tags: database.?tags ?? tags - zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : true - elasticPoolId: contains(database, 'elasticPoolId') ? database.elasticPoolId : '' - backupShortTermRetentionPolicy: contains(database, 'backupShortTermRetentionPolicy') - ? database.backupShortTermRetentionPolicy - : {} - backupLongTermRetentionPolicy: contains(database, 'backupLongTermRetentionPolicy') - ? database.backupLongTermRetentionPolicy - : {} - createMode: contains(database, 'createMode') ? database.createMode : 'Default' - sourceDatabaseResourceId: contains(database, 'sourceDatabaseResourceId') ? database.sourceDatabaseResourceId : '' - sourceDatabaseDeletionDate: contains(database, 'sourceDatabaseDeletionDate') - ? database.sourceDatabaseDeletionDate - : '' - recoveryServicesRecoveryPointResourceId: contains(database, 'recoveryServicesRecoveryPointResourceId') - ? database.recoveryServicesRecoveryPointResourceId - : '' - restorePointInTime: contains(database, 'restorePointInTime') ? database.restorePointInTime : '' + zoneRedundant: database.?zoneRedundant ?? true + elasticPoolId: database.?elasticPoolId ?? '' + backupShortTermRetentionPolicy: database.?backupShortTermRetentionPolicy ?? {} + backupLongTermRetentionPolicy: database.?backupLongTermRetentionPolicy ?? {} + createMode: database.?createMode ?? 'Default' + sourceDatabaseResourceId: database.?sourceDatabaseResourceId ?? '' + sourceDatabaseDeletionDate: database.?sourceDatabaseDeletionDate ?? '' + recoveryServicesRecoveryPointResourceId: database.?recoveryServicesRecoveryPointResourceId ?? '' + restorePointInTime: database.?restorePointInTime ?? '' } dependsOn: [ server_elasticPools // Enables us to add databases to existing elastic pools @@ -296,19 +292,17 @@ module server_elasticPools 'elastic-pool/main.bicep' = [ params: { name: elasticPool.name serverName: server.name - databaseMaxCapacity: contains(elasticPool, 'databaseMaxCapacity') ? elasticPool.databaseMaxCapacity : 2 - databaseMinCapacity: contains(elasticPool, 'databaseMinCapacity') ? elasticPool.databaseMinCapacity : 0 + databaseMaxCapacity: elasticPool.?databaseMaxCapacity ?? 2 + databaseMinCapacity: elasticPool.?databaseMinCapacity ?? 0 highAvailabilityReplicaCount: elasticPool.?highAvailabilityReplicaCount - licenseType: contains(elasticPool, 'licenseType') ? elasticPool.licenseType : 'LicenseIncluded' - maintenanceConfigurationId: contains(elasticPool, 'maintenanceConfigurationId') - ? elasticPool.maintenanceConfigurationId - : '' - maxSizeBytes: contains(elasticPool, 'maxSizeBytes') ? elasticPool.maxSizeBytes : 34359738368 + licenseType: elasticPool.?licenseType ?? 'LicenseIncluded' + maintenanceConfigurationId: elasticPool.?maintenanceConfigurationId + maxSizeBytes: elasticPool.?maxSizeBytes ?? 34359738368 minCapacity: elasticPool.?minCapacity - 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 : true + skuCapacity: elasticPool.?skuCapacity ?? 2 + skuName: elasticPool.?skuName ?? 'GP_Gen5' + skuTier: elasticPool.?skuTier ?? 'GeneralPurpose' + zoneRedundant: elasticPool.?zoneRedundant ?? true location: location tags: elasticPool.?tags ?? tags } @@ -373,8 +367,8 @@ module server_firewallRules 'firewall-rule/main.bicep' = [ params: { name: firewallRule.name serverName: server.name - endIpAddress: contains(firewallRule, 'endIpAddress') ? firewallRule.endIpAddress : '0.0.0.0' - startIpAddress: contains(firewallRule, 'startIpAddress') ? firewallRule.startIpAddress : '0.0.0.0' + endIpAddress: firewallRule.?endIpAddress ?? '0.0.0.0' + startIpAddress: firewallRule.?startIpAddress ?? '0.0.0.0' } } ] @@ -385,9 +379,7 @@ module server_virtualNetworkRules 'virtual-network-rule/main.bicep' = [ params: { name: virtualNetworkRule.name serverName: server.name - ignoreMissingVnetServiceEndpoint: contains(virtualNetworkRule, 'ignoreMissingVnetServiceEndpoint') - ? virtualNetworkRule.ignoreMissingVnetServiceEndpoint - : false + ignoreMissingVnetServiceEndpoint: virtualNetworkRule.?ignoreMissingVnetServiceEndpoint ?? false virtualNetworkSubnetId: virtualNetworkRule.virtualNetworkSubnetId } } @@ -399,17 +391,13 @@ module server_securityAlertPolicies 'security-alert-policy/main.bicep' = [ params: { name: securityAlertPolicy.name serverName: server.name - disabledAlerts: contains(securityAlertPolicy, 'disabledAlerts') ? securityAlertPolicy.disabledAlerts : [] - emailAccountAdmins: contains(securityAlertPolicy, 'emailAccountAdmins') - ? securityAlertPolicy.emailAccountAdmins - : false - emailAddresses: contains(securityAlertPolicy, 'emailAddresses') ? securityAlertPolicy.emailAddresses : [] - retentionDays: contains(securityAlertPolicy, 'retentionDays') ? securityAlertPolicy.retentionDays : 0 - state: contains(securityAlertPolicy, 'state') ? securityAlertPolicy.state : 'Disabled' - storageAccountAccessKey: contains(securityAlertPolicy, 'storageAccountAccessKey') - ? securityAlertPolicy.storageAccountAccessKey - : '' - storageEndpoint: contains(securityAlertPolicy, 'storageEndpoint') ? securityAlertPolicy.storageEndpoint : '' + disabledAlerts: securityAlertPolicy.?disabledAlerts ?? [] + emailAccountAdmins: securityAlertPolicy.?emailAccountAdmins ?? false + emailAddresses: securityAlertPolicy.?emailAddresses ?? [] + retentionDays: securityAlertPolicy.?retentionDays ?? 0 + state: securityAlertPolicy.?state ?? 'Disabled' + storageAccountAccessKey: securityAlertPolicy.?storageAccountAccessKey ?? '' + storageEndpoint: securityAlertPolicy.?storageEndpoint ?? '' } } ] @@ -419,25 +407,12 @@ module server_vulnerabilityAssessment 'vulnerability-assessment/main.bicep' = if params: { serverName: server.name name: vulnerabilityAssessmentsObj.name - recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') - ? vulnerabilityAssessmentsObj.recurringScansEmails - : [] - recurringScansEmailSubscriptionAdmins: contains( - vulnerabilityAssessmentsObj, - 'recurringScansEmailSubscriptionAdmins' - ) - ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins - : false - recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') - ? vulnerabilityAssessmentsObj.recurringScansIsEnabled - : false + recurringScansEmails: vulnerabilityAssessmentsObj.?recurringScansEmails ?? [] + recurringScansEmailSubscriptionAdmins: vulnerabilityAssessmentsObj.?recurringScansEmailSubscriptionAdmins ?? false + recurringScansIsEnabled: vulnerabilityAssessmentsObj.?recurringScansIsEnabled ?? false storageAccountResourceId: vulnerabilityAssessmentsObj.storageAccountResourceId - useStorageAccountAccessKey: contains(vulnerabilityAssessmentsObj, 'useStorageAccountAccessKey') - ? vulnerabilityAssessmentsObj.useStorageAccountAccessKey - : false - createStorageRoleAssignment: contains(vulnerabilityAssessmentsObj, 'createStorageRoleAssignment') - ? vulnerabilityAssessmentsObj.createStorageRoleAssignment - : true + useStorageAccountAccessKey: vulnerabilityAssessmentsObj.?useStorageAccountAccessKey ?? false + createStorageRoleAssignment: vulnerabilityAssessmentsObj.?createStorageRoleAssignment ?? true } dependsOn: [ server_securityAlertPolicies @@ -450,8 +425,8 @@ module server_keys 'key/main.bicep' = [ params: { name: key.?name serverName: server.name - serverKeyType: contains(key, 'serverKeyType') ? key.serverKeyType : 'ServiceManaged' - uri: contains(key, 'uri') ? key.uri : '' + serverKeyType: key.?serverKeyType ?? 'ServiceManaged' + uri: key.?uri ?? '' } } ] @@ -461,12 +436,8 @@ module server_encryptionProtector 'encryption-protector/main.bicep' = if (!empty params: { sqlServerName: server.name serverKeyName: encryptionProtectorObj.serverKeyName - serverKeyType: contains(encryptionProtectorObj, 'serverKeyType') - ? encryptionProtectorObj.serverKeyType - : 'ServiceManaged' - autoRotationEnabled: contains(encryptionProtectorObj, 'autoRotationEnabled') - ? encryptionProtectorObj.autoRotationEnabled - : true + serverKeyType: encryptionProtectorObj.?serverKeyType ?? 'ServiceManaged' + autoRotationEnabled: encryptionProtectorObj.?autoRotationEnabled ?? true } dependsOn: [ server_keys diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index af15b03a96..66bd386f4d 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "779644860652942835" + "version": "0.29.47.4906", + "templateHash": "12721468399061919493" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -616,12 +616,24 @@ "allowedValues": [ "1.0", "1.1", - "1.2" + "1.2", + "1.3" ], "metadata": { "description": "Optional. Minimal TLS version allowed." } }, + "isIPv6Enabled": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not to enable IPv6 support for this server." + } + }, "privateEndpoints": { "$ref": "#/definitions/privateEndpointType", "metadata": { @@ -634,7 +646,8 @@ "allowedValues": [ "", "Enabled", - "Disabled" + "Disabled", + "SecuredByPerimeter" ], "metadata": { "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and neither firewall rules nor virtual network rules are set." @@ -742,7 +755,8 @@ "minimalTlsVersion": "[parameters('minimalTlsVersion')]", "primaryUserAssignedIdentityId": "[if(not(empty(parameters('primaryUserAssignedIdentityId'))), parameters('primaryUserAssignedIdentityId'), null())]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(and(not(empty(parameters('privateEndpoints'))), empty(parameters('firewallRules'))), empty(parameters('virtualNetworkRules'))), 'Disabled', null()))]", - "restrictOutboundNetworkAccess": "[if(not(empty(parameters('restrictOutboundNetworkAccess'))), parameters('restrictOutboundNetworkAccess'), null())]" + "restrictOutboundNetworkAccess": "[if(not(empty(parameters('restrictOutboundNetworkAccess'))), parameters('restrictOutboundNetworkAccess'), null())]", + "isIPv6Enabled": "[parameters('isIPv6Enabled')]" } }, "server_lock": { @@ -801,42 +815,90 @@ "serverName": { "value": "[parameters('name')]" }, - "skuTier": "[if(contains(parameters('databases')[copyIndex()], 'skuTier'), createObject('value', parameters('databases')[copyIndex()].skuTier), createObject('value', 'GeneralPurpose'))]", - "skuName": "[if(contains(parameters('databases')[copyIndex()], 'skuName'), createObject('value', parameters('databases')[copyIndex()].skuName), createObject('value', 'GP_Gen5_2'))]", + "skuTier": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'skuTier'), 'GeneralPurpose')]" + }, + "skuName": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'skuName'), 'GP_Gen5_2')]" + }, "skuCapacity": { "value": "[tryGet(parameters('databases')[copyIndex()], 'skuCapacity')]" }, - "skuFamily": "[if(contains(parameters('databases')[copyIndex()], 'skuFamily'), createObject('value', parameters('databases')[copyIndex()].skuFamily), createObject('value', ''))]", - "skuSize": "[if(contains(parameters('databases')[copyIndex()], 'skuSize'), createObject('value', parameters('databases')[copyIndex()].skuSize), createObject('value', ''))]", - "collation": "[if(contains(parameters('databases')[copyIndex()], 'collation'), createObject('value', parameters('databases')[copyIndex()].collation), createObject('value', 'SQL_Latin1_General_CP1_CI_AS'))]", - "maxSizeBytes": "[if(contains(parameters('databases')[copyIndex()], 'maxSizeBytes'), createObject('value', parameters('databases')[copyIndex()].maxSizeBytes), createObject('value', json('34359738368')))]", - "autoPauseDelay": "[if(contains(parameters('databases')[copyIndex()], 'autoPauseDelay'), createObject('value', parameters('databases')[copyIndex()].autoPauseDelay), createObject('value', 0))]", + "skuFamily": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'skuFamily'), '')]" + }, + "skuSize": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'skuSize'), '')]" + }, + "collation": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'collation'), 'SQL_Latin1_General_CP1_CI_AS')]" + }, + "maxSizeBytes": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'maxSizeBytes'), json('34359738368'))]" + }, + "autoPauseDelay": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'autoPauseDelay'), 0)]" + }, "diagnosticSettings": { "value": "[tryGet(parameters('databases')[copyIndex()], 'diagnosticSettings')]" }, - "isLedgerOn": "[if(contains(parameters('databases')[copyIndex()], 'isLedgerOn'), createObject('value', parameters('databases')[copyIndex()].isLedgerOn), createObject('value', false()))]", + "isLedgerOn": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'isLedgerOn'), false())]" + }, "location": { "value": "[parameters('location')]" }, - "licenseType": "[if(contains(parameters('databases')[copyIndex()], 'licenseType'), createObject('value', parameters('databases')[copyIndex()].licenseType), createObject('value', ''))]", - "maintenanceConfigurationId": "[if(contains(parameters('databases')[copyIndex()], 'maintenanceConfigurationId'), createObject('value', parameters('databases')[copyIndex()].maintenanceConfigurationId), createObject('value', ''))]", - "minCapacity": "[if(contains(parameters('databases')[copyIndex()], 'minCapacity'), createObject('value', parameters('databases')[copyIndex()].minCapacity), createObject('value', ''))]", - "highAvailabilityReplicaCount": "[if(contains(parameters('databases')[copyIndex()], 'highAvailabilityReplicaCount'), createObject('value', parameters('databases')[copyIndex()].highAvailabilityReplicaCount), createObject('value', 0))]", - "readScale": "[if(contains(parameters('databases')[copyIndex()], 'readScale'), createObject('value', parameters('databases')[copyIndex()].readScale), createObject('value', 'Disabled'))]", - "requestedBackupStorageRedundancy": "[if(contains(parameters('databases')[copyIndex()], 'requestedBackupStorageRedundancy'), createObject('value', parameters('databases')[copyIndex()].requestedBackupStorageRedundancy), createObject('value', ''))]", - "sampleName": "[if(contains(parameters('databases')[copyIndex()], 'sampleName'), createObject('value', parameters('databases')[copyIndex()].sampleName), createObject('value', ''))]", + "licenseType": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'licenseType'), '')]" + }, + "maintenanceConfigurationId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'maintenanceConfigurationId')]" + }, + "minCapacity": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'minCapacity'), '')]" + }, + "highAvailabilityReplicaCount": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'highAvailabilityReplicaCount'), 0)]" + }, + "readScale": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'readScale'), 'Disabled')]" + }, + "requestedBackupStorageRedundancy": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'requestedBackupStorageRedundancy'), '')]" + }, + "sampleName": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'sampleName'), '')]" + }, "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', 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()))]", - "createMode": "[if(contains(parameters('databases')[copyIndex()], 'createMode'), createObject('value', parameters('databases')[copyIndex()].createMode), createObject('value', 'Default'))]", - "sourceDatabaseResourceId": "[if(contains(parameters('databases')[copyIndex()], 'sourceDatabaseResourceId'), createObject('value', parameters('databases')[copyIndex()].sourceDatabaseResourceId), createObject('value', ''))]", - "sourceDatabaseDeletionDate": "[if(contains(parameters('databases')[copyIndex()], 'sourceDatabaseDeletionDate'), createObject('value', parameters('databases')[copyIndex()].sourceDatabaseDeletionDate), createObject('value', ''))]", - "recoveryServicesRecoveryPointResourceId": "[if(contains(parameters('databases')[copyIndex()], 'recoveryServicesRecoveryPointResourceId'), createObject('value', parameters('databases')[copyIndex()].recoveryServicesRecoveryPointResourceId), createObject('value', ''))]", - "restorePointInTime": "[if(contains(parameters('databases')[copyIndex()], 'restorePointInTime'), createObject('value', parameters('databases')[copyIndex()].restorePointInTime), createObject('value', ''))]" + "zoneRedundant": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'zoneRedundant'), true())]" + }, + "elasticPoolId": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'elasticPoolId'), '')]" + }, + "backupShortTermRetentionPolicy": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicy'), createObject())]" + }, + "backupLongTermRetentionPolicy": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicy'), createObject())]" + }, + "createMode": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'createMode'), 'Default')]" + }, + "sourceDatabaseResourceId": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseResourceId'), '')]" + }, + "sourceDatabaseDeletionDate": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseDeletionDate'), '')]" + }, + "recoveryServicesRecoveryPointResourceId": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'recoveryServicesRecoveryPointResourceId'), '')]" + }, + "restorePointInTime": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'restorePointInTime'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -845,8 +907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11407307502843440892" + "version": "0.29.47.4906", + "templateHash": "18021918128213514276" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -1195,7 +1257,7 @@ }, "maintenanceConfigurationId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur." } @@ -1244,7 +1306,7 @@ "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", "isLedgerOn": "[parameters('isLedgerOn')]", - "maintenanceConfigurationId": "[if(not(empty(parameters('maintenanceConfigurationId'))), parameters('maintenanceConfigurationId'), null())]", + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", "elasticPoolId": "[parameters('elasticPoolId')]", "createMode": "[parameters('createMode')]", "sourceDatabaseId": "[if(not(empty(parameters('sourceDatabaseResourceId'))), parameters('sourceDatabaseResourceId'), null())]", @@ -1314,8 +1376,12 @@ "databaseName": { "value": "[parameters('name')]" }, - "diffBackupIntervalInHours": "[if(contains(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours'), createObject('value', parameters('backupShortTermRetentionPolicy').diffBackupIntervalInHours), createObject('value', 24))]", - "retentionDays": "[if(contains(parameters('backupShortTermRetentionPolicy'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPolicy').retentionDays), createObject('value', 7))]" + "diffBackupIntervalInHours": { + "value": "[coalesce(tryGet(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours'), 24)]" + }, + "retentionDays": { + "value": "[coalesce(tryGet(parameters('backupShortTermRetentionPolicy'), 'retentionDays'), 7)]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1323,8 +1389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10836519140305169908" + "version": "0.29.47.4906", + "templateHash": "8635162595153731245" }, "name": "Azure SQL Server Database Short Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy.", @@ -1414,10 +1480,18 @@ "databaseName": { "value": "[parameters('name')]" }, - "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').weeklyRetention), createObject('value', ''))]", - "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').monthlyRetention), createObject('value', ''))]", - "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPolicy').yearlyRetention), createObject('value', ''))]", - "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPolicy'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPolicy').weekOfYear), createObject('value', 1))]" + "weeklyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention'), '')]" + }, + "monthlyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention'), '')]" + }, + "yearlyRetention": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention'), '')]" + }, + "weekOfYear": { + "value": "[coalesce(tryGet(parameters('backupLongTermRetentionPolicy'), 'weekOfYear'), 1)]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1425,8 +1499,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10064519186693398262" + "version": "0.29.47.4906", + "templateHash": "2778016138108001251" }, "name": "SQL Server Database Long Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy.", @@ -1574,21 +1648,39 @@ "serverName": { "value": "[parameters('name')]" }, - "databaseMaxCapacity": "[if(contains(parameters('elasticPools')[copyIndex()], 'databaseMaxCapacity'), createObject('value', parameters('elasticPools')[copyIndex()].databaseMaxCapacity), createObject('value', 2))]", - "databaseMinCapacity": "[if(contains(parameters('elasticPools')[copyIndex()], 'databaseMinCapacity'), createObject('value', parameters('elasticPools')[copyIndex()].databaseMinCapacity), createObject('value', 0))]", + "databaseMaxCapacity": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'databaseMaxCapacity'), 2)]" + }, + "databaseMinCapacity": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'databaseMinCapacity'), 0)]" + }, "highAvailabilityReplicaCount": { "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'highAvailabilityReplicaCount')]" }, - "licenseType": "[if(contains(parameters('elasticPools')[copyIndex()], 'licenseType'), createObject('value', parameters('elasticPools')[copyIndex()].licenseType), createObject('value', 'LicenseIncluded'))]", - "maintenanceConfigurationId": "[if(contains(parameters('elasticPools')[copyIndex()], 'maintenanceConfigurationId'), createObject('value', parameters('elasticPools')[copyIndex()].maintenanceConfigurationId), createObject('value', ''))]", - "maxSizeBytes": "[if(contains(parameters('elasticPools')[copyIndex()], 'maxSizeBytes'), createObject('value', parameters('elasticPools')[copyIndex()].maxSizeBytes), createObject('value', json('34359738368')))]", + "licenseType": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'licenseType'), 'LicenseIncluded')]" + }, + "maintenanceConfigurationId": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'maintenanceConfigurationId')]" + }, + "maxSizeBytes": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'maxSizeBytes'), json('34359738368'))]" + }, "minCapacity": { "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'minCapacity')]" }, - "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', true()))]", + "skuCapacity": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'skuCapacity'), 2)]" + }, + "skuName": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'skuName'), 'GP_Gen5')]" + }, + "skuTier": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'skuTier'), 'GeneralPurpose')]" + }, + "zoneRedundant": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'zoneRedundant'), true())]" + }, "location": { "value": "[parameters('location')]" }, @@ -1603,8 +1695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1486548652639885128" + "version": "0.29.47.4906", + "templateHash": "17774091526328280898" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -1678,7 +1770,7 @@ }, "maintenanceConfigurationId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Maintenance configuration resource ID assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." } @@ -2576,8 +2668,12 @@ "serverName": { "value": "[parameters('name')]" }, - "endIpAddress": "[if(contains(parameters('firewallRules')[copyIndex()], 'endIpAddress'), createObject('value', parameters('firewallRules')[copyIndex()].endIpAddress), createObject('value', '0.0.0.0'))]", - "startIpAddress": "[if(contains(parameters('firewallRules')[copyIndex()], 'startIpAddress'), createObject('value', parameters('firewallRules')[copyIndex()].startIpAddress), createObject('value', '0.0.0.0'))]" + "endIpAddress": { + "value": "[coalesce(tryGet(parameters('firewallRules')[copyIndex()], 'endIpAddress'), '0.0.0.0')]" + }, + "startIpAddress": { + "value": "[coalesce(tryGet(parameters('firewallRules')[copyIndex()], 'startIpAddress'), '0.0.0.0')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2585,8 +2681,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6449556555046717103" + "version": "0.29.47.4906", + "templateHash": "7779473510493338097" }, "name": "Azure SQL Server Firewall Rule", "description": "This module deploys an Azure SQL Server Firewall Rule.", @@ -2680,7 +2776,9 @@ "serverName": { "value": "[parameters('name')]" }, - "ignoreMissingVnetServiceEndpoint": "[if(contains(parameters('virtualNetworkRules')[copyIndex()], 'ignoreMissingVnetServiceEndpoint'), createObject('value', parameters('virtualNetworkRules')[copyIndex()].ignoreMissingVnetServiceEndpoint), createObject('value', false()))]", + "ignoreMissingVnetServiceEndpoint": { + "value": "[coalesce(tryGet(parameters('virtualNetworkRules')[copyIndex()], 'ignoreMissingVnetServiceEndpoint'), false())]" + }, "virtualNetworkSubnetId": { "value": "[parameters('virtualNetworkRules')[copyIndex()].virtualNetworkSubnetId]" } @@ -2691,8 +2789,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4969955763304077350" + "version": "0.29.47.4906", + "templateHash": "7859066741604114060" }, "name": "Azure SQL Server Virtual Network Rules", "description": "This module deploys an Azure SQL Server Virtual Network Rule.", @@ -2785,13 +2883,27 @@ "serverName": { "value": "[parameters('name')]" }, - "disabledAlerts": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'disabledAlerts'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].disabledAlerts), createObject('value', createArray()))]", - "emailAccountAdmins": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'emailAccountAdmins'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].emailAccountAdmins), createObject('value', false()))]", - "emailAddresses": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'emailAddresses'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].emailAddresses), createObject('value', createArray()))]", - "retentionDays": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'retentionDays'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].retentionDays), createObject('value', 0))]", - "state": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'state'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].state), createObject('value', 'Disabled'))]", - "storageAccountAccessKey": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'storageAccountAccessKey'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].storageAccountAccessKey), createObject('value', ''))]", - "storageEndpoint": "[if(contains(parameters('securityAlertPolicies')[copyIndex()], 'storageEndpoint'), createObject('value', parameters('securityAlertPolicies')[copyIndex()].storageEndpoint), createObject('value', ''))]" + "disabledAlerts": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'disabledAlerts'), createArray())]" + }, + "emailAccountAdmins": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAccountAdmins'), false())]" + }, + "emailAddresses": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAddresses'), createArray())]" + }, + "retentionDays": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'retentionDays'), 0)]" + }, + "state": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'state'), 'Disabled')]" + }, + "storageAccountAccessKey": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageAccountAccessKey'), '')]" + }, + "storageEndpoint": { + "value": "[coalesce(tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageEndpoint'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2799,8 +2911,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15406914222375641032" + "version": "0.29.47.4906", + "templateHash": "6025191760768766090" }, "name": "Azure SQL Server Security Alert Policies", "description": "This module deploys an Azure SQL Server Security Alert Policy.", @@ -2935,14 +3047,24 @@ "name": { "value": "[parameters('vulnerabilityAssessmentsObj').name]" }, - "recurringScansEmails": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmails'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmails), createObject('value', createArray()))]", - "recurringScansEmailSubscriptionAdmins": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmailSubscriptionAdmins'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmailSubscriptionAdmins), createObject('value', false()))]", - "recurringScansIsEnabled": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansIsEnabled'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansIsEnabled), createObject('value', false()))]", + "recurringScansEmails": { + "value": "[coalesce(tryGet(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmails'), createArray())]" + }, + "recurringScansEmailSubscriptionAdmins": { + "value": "[coalesce(tryGet(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmailSubscriptionAdmins'), false())]" + }, + "recurringScansIsEnabled": { + "value": "[coalesce(tryGet(parameters('vulnerabilityAssessmentsObj'), 'recurringScansIsEnabled'), false())]" + }, "storageAccountResourceId": { "value": "[parameters('vulnerabilityAssessmentsObj').storageAccountResourceId]" }, - "useStorageAccountAccessKey": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey'), createObject('value', parameters('vulnerabilityAssessmentsObj').useStorageAccountAccessKey), createObject('value', false()))]", - "createStorageRoleAssignment": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment'), createObject('value', parameters('vulnerabilityAssessmentsObj').createStorageRoleAssignment), createObject('value', true()))]" + "useStorageAccountAccessKey": { + "value": "[coalesce(tryGet(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey'), false())]" + }, + "createStorageRoleAssignment": { + "value": "[coalesce(tryGet(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment'), true())]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2950,8 +3072,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11004049200994426011" + "version": "0.29.47.4906", + "templateHash": "5682596516926040129" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -3053,8 +3175,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13956215614091387428" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { @@ -3132,8 +3254,12 @@ "serverName": { "value": "[parameters('name')]" }, - "serverKeyType": "[if(contains(parameters('keys')[copyIndex()], 'serverKeyType'), createObject('value', parameters('keys')[copyIndex()].serverKeyType), createObject('value', 'ServiceManaged'))]", - "uri": "[if(contains(parameters('keys')[copyIndex()], 'uri'), createObject('value', parameters('keys')[copyIndex()].uri), createObject('value', ''))]" + "serverKeyType": { + "value": "[coalesce(tryGet(parameters('keys')[copyIndex()], 'serverKeyType'), 'ServiceManaged')]" + }, + "uri": { + "value": "[coalesce(tryGet(parameters('keys')[copyIndex()], 'uri'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -3142,8 +3268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17839617504395216689" + "version": "0.29.47.4906", + "templateHash": "5863771213375512760" }, "name": "Azure SQL Server Keys", "description": "This module deploys an Azure SQL Server Key.", @@ -3252,8 +3378,12 @@ "serverKeyName": { "value": "[parameters('encryptionProtectorObj').serverKeyName]" }, - "serverKeyType": "[if(contains(parameters('encryptionProtectorObj'), 'serverKeyType'), createObject('value', parameters('encryptionProtectorObj').serverKeyType), createObject('value', 'ServiceManaged'))]", - "autoRotationEnabled": "[if(contains(parameters('encryptionProtectorObj'), 'autoRotationEnabled'), createObject('value', parameters('encryptionProtectorObj').autoRotationEnabled), createObject('value', true()))]" + "serverKeyType": { + "value": "[coalesce(tryGet(parameters('encryptionProtectorObj'), 'serverKeyType'), 'ServiceManaged')]" + }, + "autoRotationEnabled": { + "value": "[coalesce(tryGet(parameters('encryptionProtectorObj'), 'autoRotationEnabled'), true())]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -3261,8 +3391,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11473914706327458055" + "version": "0.29.47.4906", + "templateHash": "6914924378490463775" }, "name": "Azure SQL Server Encryption Protector", "description": "This module deploys an Azure SQL Server Encryption Protector.", @@ -3394,8 +3524,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2456263707393734456" + "version": "0.29.47.4906", + "templateHash": "4165841300638093382" }, "name": "Azure SQL Server Audit Settings", "description": "This module deploys an Azure SQL Server Audit Settings.", @@ -3533,8 +3663,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13956215614091387428" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { @@ -3621,8 +3751,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16142913599202614386" + "version": "0.29.47.4906", + "templateHash": "594547303316002116" } }, "definitions": { diff --git a/avm/res/sql/server/tests/e2e/max/main.test.bicep b/avm/res/sql/server/tests/e2e/max/main.test.bicep index ec70abad1a..e95f51ae35 100644 --- a/avm/res/sql/server/tests/e2e/max/main.test.bicep +++ b/avm/res/sql/server/tests/e2e/max/main.test.bicep @@ -11,8 +11,9 @@ metadata description = 'This instance deploys the module with most of its featur @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-sql.servers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// Enforce uksouth to avoid restrictions around zone redundancy in certain regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' @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 = 'sqlsmax' @@ -32,17 +33,17 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -50,13 +51,13 @@ module nestedDependencies 'dependencies.bicep' = { // =========== module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -66,7 +67,7 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' params: { name: '${namePrefix}-${serviceShort}' lock: { @@ -76,7 +77,7 @@ module testDeployment '../../../main.bicep' = { primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId administratorLogin: 'adminUserName' administratorLoginPassword: password - location: resourceLocation + location: enforcedLocation roleAssignments: [ { name: '7027a5c5-d1b1-49e0-80cc-ffdff3a3ada9' @@ -115,8 +116,6 @@ module testDeployment '../../../main.bicep' = { skuName: 'GP_Gen5' skuTier: 'GeneralPurpose' skuCapacity: 10 - // Pre-existing 'public' configuration - maintenanceConfigurationId: '${subscription().id}/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_${resourceLocation}_DB_1' } ] databases: [ diff --git a/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep index 2f7e279f3b..3777c51c2c 100644 --- a/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep @@ -11,8 +11,9 @@ metadata description = 'This instance deploys the module in alignment with the b @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-sql.servers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// Enforce uksouth to avoid restrictions around zone redundancy in certain regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' @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 = 'sqlswaf' @@ -28,17 +29,17 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -46,13 +47,13 @@ module nestedDependencies 'dependencies.bicep' = { // =========== module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -62,7 +63,7 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' params: { name: '${namePrefix}-${serviceShort}' primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId @@ -73,7 +74,7 @@ module testDeployment '../../../main.bicep' = { principalType: 'Application' tenantId: tenant().tenantId } - location: resourceLocation + location: enforcedLocation vulnerabilityAssessmentsObj: { name: 'default' emailSubscriptionAdmins: true @@ -90,8 +91,7 @@ module testDeployment '../../../main.bicep' = { skuName: 'GP_Gen5' skuTier: 'GeneralPurpose' skuCapacity: 10 - // Pre-existing 'public' configuration - maintenanceConfigurationId: '${subscription().id}/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_${resourceLocation}_DB_1' + maintenanceConfigurationId: '${subscription().id}/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_${enforcedLocation}_DB_1' } ] databases: [ diff --git a/avm/res/sql/server/version.json b/avm/res/sql/server/version.json index 0f81d22abc..b8b30a0125 100644 --- a/avm/res/sql/server/version.json +++ b/avm/res/sql/server/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ]