diff --git a/arm/Microsoft.AppConfiguration/configurationStores/.bicep/nested_privateEndpoint.bicep b/arm/Microsoft.AppConfiguration/configurationStores/.bicep/nested_privateEndpoint.bicep new file mode 100644 index 0000000000..0e3f625a39 --- /dev/null +++ b/arm/Microsoft.AppConfiguration/configurationStores/.bicep/nested_privateEndpoint.bicep @@ -0,0 +1,49 @@ +param privateEndpointResourceId string +param privateEndpointVnetLocation string +param privateEndpointObj object +param tags object + +var privateEndpointResourceName = last(split(privateEndpointResourceId, '/')) +var privateEndpoint_var = { + name: (contains(privateEndpointObj, 'name') ? (empty(privateEndpointObj.name) ? '${privateEndpointResourceName}-${privateEndpointObj.service}' : privateEndpointObj.name) : '${privateEndpointResourceName}-${privateEndpointObj.service}') + subnetResourceId: privateEndpointObj.subnetResourceId + service: [ + privateEndpointObj.service + ] + privateDnsZoneResourceIds: (contains(privateEndpointObj, 'privateDnsZoneResourceIds') ? (empty(privateEndpointObj.privateDnsZoneResourceIds) ? [] : privateEndpointObj.privateDnsZoneResourceIds) : []) + customDnsConfigs: (contains(privateEndpointObj, 'customDnsConfigs') ? (empty(privateEndpointObj.customDnsConfigs) ? null : privateEndpointObj.customDnsConfigs) : null) +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-05-01' = { + name: privateEndpoint_var.name + location: privateEndpointVnetLocation + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: privateEndpoint_var.name + properties: { + privateLinkServiceId: privateEndpointResourceId + groupIds: privateEndpoint_var.service + } + } + ] + manualPrivateLinkServiceConnections: [] + subnet: { + id: privateEndpoint_var.subnetResourceId + } + customDnsConfigs: privateEndpoint_var.customDnsConfigs + } +} + +resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-02-01' = if (!empty(privateEndpoint_var.privateDnsZoneResourceIds)) { + name: '${privateEndpoint.name}/default' + properties: { + privateDnsZoneConfigs: [for j in range(0, length(privateEndpoint_var.privateDnsZoneResourceIds)): { + name: last(split(privateEndpoint_var.privateDnsZoneResourceIds[j], '/')) + properties: { + privateDnsZoneId: privateEndpoint_var.privateDnsZoneResourceIds[j] + } + }] + } +} diff --git a/arm/Microsoft.AppConfiguration/configurationStores/.parameters/parameters.json b/arm/Microsoft.AppConfiguration/configurationStores/.parameters/parameters.json index c0fadfe1eb..a1bdb8cac9 100644 --- a/arm/Microsoft.AppConfiguration/configurationStores/.parameters/parameters.json +++ b/arm/Microsoft.AppConfiguration/configurationStores/.parameters/parameters.json @@ -34,7 +34,7 @@ ] }, "createMode": { - "value": "Recover" + "value": "Default" }, "disableLocalAuth": { "value": false @@ -47,6 +47,14 @@ }, "softDeleteRetentionInDays": { "value": 1 + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "configurationStores" + } + ] } } } diff --git a/arm/Microsoft.AppConfiguration/configurationStores/deploy.bicep b/arm/Microsoft.AppConfiguration/configurationStores/deploy.bicep index a7fb250d2b..17aad36691 100644 --- a/arm/Microsoft.AppConfiguration/configurationStores/deploy.bicep +++ b/arm/Microsoft.AppConfiguration/configurationStores/deploy.bicep @@ -97,6 +97,9 @@ param diagnosticMetricsToEnable array = [ @description('Optional. The name of the diagnostic setting, if deployed.') param diagnosticSettingsName string = '${name}-diagnosticSettings' +@description('Optional. Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { category: category enabled: true @@ -185,6 +188,16 @@ module appConfiguration_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, } }] +module appConfiguration_privateEndpoints '.bicep/nested_privateEndpoint.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-AppConfig-PrivateEndpoint-${index}' + params: { + privateEndpointResourceId: appConfiguration.id + privateEndpointVnetLocation: empty(privateEndpoints) ? 'dummy' : reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + privateEndpointObj: privateEndpoint + tags: tags + } +}] + @description('The name of the app configuration.') output name string = appConfiguration.name diff --git a/arm/Microsoft.AppConfiguration/configurationStores/readme.md b/arm/Microsoft.AppConfiguration/configurationStores/readme.md index 30bd87f57b..2bfd66b882 100644 --- a/arm/Microsoft.AppConfiguration/configurationStores/readme.md +++ b/arm/Microsoft.AppConfiguration/configurationStores/readme.md @@ -16,6 +16,8 @@ This module deploys an App Configuration Store. | `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) | ## Parameters @@ -42,6 +44,7 @@ This module deploys an App Configuration Store. | `enablePurgeProtection` | bool | `False` | | Property specifying whether protection against purge is enabled for this configuration store. | | `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. | | `publicNetworkAccess` | string | `'Enabled'` | `[Disabled, Enabled]` | Control permission for data plane traffic coming from public networks while private endpoint is enabled. | | `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'. | | `softDeleteRetentionInDays` | int | `1` | | The amount of time in days that the configuration store will be retained when it is soft deleted. | @@ -106,6 +109,42 @@ Create a role assignment for the given resource. If you want to assign a service } ``` +### 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 |