diff --git a/arm/Microsoft.Sql/servers/.bicep/nested_privateEndpoint.bicep b/arm/Microsoft.Sql/servers/.bicep/nested_privateEndpoint.bicep new file mode 100644 index 0000000000..7af99c7273 --- /dev/null +++ b/arm/Microsoft.Sql/servers/.bicep/nested_privateEndpoint.bicep @@ -0,0 +1,58 @@ +@description('The resource ID of the service to link to') +param privateEndpointResourceId string + +@description('Required. The location of the proviate endpoint') +param privateEndpointVnetLocation string + +@description('Optional. Tags to add to the private endpoint.') +param tags object = {} + +@description('Optional. The name of the private endpoint') +param name string = '${last(split(privateEndpointResourceId, '/'))}-${service}' + +@description('Required. The service/groupId his private endpoint should connect to') +param service string = 'sqlServer' + +@description('Required. Subnet in a virtual network resource.') +param subnetResourceId string + +@description('Optional. Custom DNS configurations.') +param customDnsConfigs array = [] + +@description('Optional. A collection of private DNS zone configurations of the private dns zone group.') +param privateDnsZoneResourceIds array = [] + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-05-01' = { + name: name + location: privateEndpointVnetLocation + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: privateEndpointResourceId + groupIds: [ + service + ] + } + } + ] + subnet: { + id: subnetResourceId + } + customDnsConfigs: customDnsConfigs + } + + resource privateDnsZoneGroups 'privateDnsZoneGroups@2021-02-01' = { + name: 'default' + properties: { + privateDnsZoneConfigs: [for privateDnsZoneResourceId in privateDnsZoneResourceIds: { + name: last(split(privateDnsZoneResourceId, '/')) + properties: { + privateDnsZoneId: privateDnsZoneResourceId + } + }] + } + } +} diff --git a/arm/Microsoft.Sql/servers/.parameters/parameters.json b/arm/Microsoft.Sql/servers/.parameters/parameters.json index 25727edea5..2f3893f12f 100644 --- a/arm/Microsoft.Sql/servers/.parameters/parameters.json +++ b/arm/Microsoft.Sql/servers/.parameters/parameters.json @@ -88,6 +88,13 @@ "value": { "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] } } } diff --git a/arm/Microsoft.Sql/servers/deploy.bicep b/arm/Microsoft.Sql/servers/deploy.bicep index 825bd6a492..a34a078f92 100644 --- a/arm/Microsoft.Sql/servers/deploy.bicep +++ b/arm/Microsoft.Sql/servers/deploy.bicep @@ -46,6 +46,9 @@ param securityAlertPolicies array = [] @description('Conditional. The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided.') param administrators object = {} +@description('Optional. Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') var identity = identityType != 'None' ? { @@ -143,6 +146,20 @@ module server_databases 'databases/deploy.bicep' = [for (database, index) in dat } }] +module server_privateEndpoints '.bicep/nested_privateEndpoint.bicep' = [for (endpoint, index) in privateEndpoints: if (!empty(privateEndpoints)) { + name: '${uniqueString(deployment().name, location)}-Sql-PrivateEndpoints-${index}' + params: { + privateEndpointResourceId: server.id + privateEndpointVnetLocation: !empty(privateEndpoints) ? reference(split(endpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location : 'dummy' + service: contains(endpoint, 'service') ? endpoint.service : 'sqlServer' + subnetResourceId: endpoint.subnetResourceId + customDnsConfigs: contains(endpoint, 'customDnsConfigs') ? endpoint.customDnsConfigs : [] + name: contains(endpoint, 'name') ? endpoint.name : '${last(split(server.id, '/'))}-sql' + privateDnsZoneResourceIds: contains(endpoint, 'privateDnsZoneResourceIds') ? endpoint.privateDnsZoneResourceIds : [] + tags: contains(endpoint, 'tags') ? endpoint.tags : {} + } +}] + module server_firewallRules 'firewallRules/deploy.bicep' = [for (firewallRule, index) in firewallRules: { name: '${uniqueString(deployment().name, location)}-Sql-FirewallRules-${index}' params: { diff --git a/arm/Microsoft.Sql/servers/readme.md b/arm/Microsoft.Sql/servers/readme.md index 8e8b45f566..53da77112c 100644 --- a/arm/Microsoft.Sql/servers/readme.md +++ b/arm/Microsoft.Sql/servers/readme.md @@ -15,6 +15,8 @@ This module deploys a SQL server. | `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2020-10-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-10-01-preview/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-05-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-02-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Sql/servers` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2021-05-01-preview/servers) | | `Microsoft.Sql/servers/databases` | [2021-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2021-02-01-preview/servers/databases) | | `Microsoft.Sql/servers/firewallRules` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2021-05-01-preview/servers/firewallRules) | @@ -43,6 +45,7 @@ This module deploys a SQL server. | `firewallRules` | _[firewallRules](firewallRules/readme.md)_ array | `[]` | | The firewall rules to create in the server. | | `location` | string | `[resourceGroup().location]` | | Location for all resources. | | `lock` | string | `'NotSpecified'` | `[CanNotDelete, NotSpecified, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | | `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' 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'. | | `securityAlertPolicies` | _[securityAlertPolicies](securityAlertPolicies/readme.md)_ array | `[]` | | The security alert policies to create in the server. | | `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | @@ -124,6 +127,42 @@ https://docs.microsoft.com/en-us/azure/templates/microsoft.sql/servers/administr } ``` +### Parameter Usage: `privateEndpoints` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "<>" // e.g. vault, registry, file, blob, queue, table etc. + "privateDnsZoneResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ], + "customDnsConfigs": [ // Optional + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "<>" // e.g. vault, registry, file, blob, queue, table etc. + } + ] +} +``` + ## Outputs | Output Name | Type | Description |