diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 8e3390eed2..475a9544ee 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -445,6 +445,48 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'blob' + subnetResourceId: '' + } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'table' + subnetResourceId: '' + } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'queue' + subnetResourceId: '' + } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'file' + subnetResourceId: '' + } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'web' + subnetResourceId: '' + } + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'dfs' + subnetResourceId: '' + } ] queueServices: { diagnosticSettings: [ @@ -841,6 +883,48 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "blob", + "subnetResourceId": "" + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "table", + "subnetResourceId": "" + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "queue", + "subnetResourceId": "" + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "file", + "subnetResourceId": "" + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "web", + "subnetResourceId": "" + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "dfs", + "subnetResourceId": "" } ] }, @@ -2544,9 +2628,10 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | Manual PrivateLink Service Connections. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | @@ -2673,6 +2758,13 @@ A private ip address obtained from the private endpoint's subnet. - Required: Yes - Type: string +### Parameter: `privateEndpoints.isManualConnection` + +Manual PrivateLink Service Connections. + +- Required: No +- Type: bool + ### Parameter: `privateEndpoints.location` The location to deploy the private endpoint to. @@ -2716,12 +2808,12 @@ Specify the name of lock. - Required: No - Type: string -### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections` +### Parameter: `privateEndpoints.manualConnectionRequestMessage` -Manual PrivateLink Service Connections. +A message passed to the owner of the remote resource with the manual connection request. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.name` @@ -3031,7 +3123,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.3.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | ## Notes diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 83e82523a8..4ad5eaca32 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -353,12 +353,13 @@ resource storageAccount_roleAssignments 'Microsoft.Authorization/roleAssignments scope: storageAccount }] -module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.1' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { +module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-StorageAccount-PrivateEndpoint-${index}' params: { - privateLinkServiceConnections: [ + name: privateEndpoint.?name ?? 'pep-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' + privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true ? [ { - name: name + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' properties: { privateLinkServiceId: storageAccount.id groupIds: [ @@ -366,8 +367,19 @@ module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoi ] } } - ] - name: privateEndpoint.?name ?? 'pep-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' + ] : null + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + privateEndpoint.service + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] : null subnetResourceId: privateEndpoint.subnetResourceId enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location @@ -376,7 +388,6 @@ module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoi privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections customDnsConfigs: privateEndpoint.?customDnsConfigs ipConfigurations: privateEndpoint.?ipConfigurations applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds @@ -571,6 +582,13 @@ type privateEndpointType = { @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? + @description('Optional. Manual PrivateLink Service Connections.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + @description('Optional. Custom DNS configurations.') customDnsConfigs: { @description('Required. Fqdn that resolves to private endpoint ip address.') @@ -613,9 +631,6 @@ type privateEndpointType = { @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') tags: object? - @description('Optional. Manual PrivateLink Service Connections.') - manualPrivateLinkServiceConnections: array? - @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? }[]? diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 1b3b8db125..12560b4a3b 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "6887152386156436186" + "version": "0.25.53.49325", + "templateHash": "8715806181703033501" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -244,6 +244,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -352,13 +367,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -1012,22 +1020,11 @@ }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]", - "groupIds": [ - "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service]" - ] - } - } - ] - }, "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -1052,9 +1049,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -1075,8 +1069,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1125,7 +1119,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -1203,7 +1197,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -1306,7 +1300,7 @@ "fqdn": { "type": "string", "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -1315,7 +1309,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -1441,7 +1435,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1546,8 +1540,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1658,6 +1652,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } @@ -1690,8 +1691,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "3500765950730474634" + "version": "0.25.53.49325", + "templateHash": "774635646248081518" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", @@ -1800,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "10424013943333306181" + "version": "0.25.53.49325", + "templateHash": "17117452291389534361" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", @@ -2018,8 +2019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "15608511572128600175" + "version": "0.25.53.49325", + "templateHash": "4746835745355709536" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2416,8 +2417,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "4692605911774255516" + "version": "0.25.53.49325", + "templateHash": "11351273351916968732" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2685,8 +2686,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "18172078163140627549" + "version": "0.25.53.49325", + "templateHash": "10329548264889228160" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", @@ -2864,8 +2865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "4598732349199400100" + "version": "0.25.53.49325", + "templateHash": "5288106279947156711" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3145,8 +3146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "17147893039694736501" + "version": "0.25.53.49325", + "templateHash": "2026482374148202879" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3345,8 +3346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "7361754328987495524" + "version": "0.25.53.49325", + "templateHash": "15595600813016932186" } }, "parameters": { @@ -3614,8 +3615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "3078597772679428481" + "version": "0.25.53.49325", + "templateHash": "14446905482734328346" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -3859,8 +3860,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "15723904499918946832" + "version": "0.25.53.49325", + "templateHash": "12411465246702614738" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -4116,8 +4117,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "2482892338912812073" + "version": "0.25.53.49325", + "templateHash": "14471460242481977546" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4358,8 +4359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.3.34343", - "templateHash": "1839032011015561279" + "version": "0.25.53.49325", + "templateHash": "8824223095963877860" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/tests/e2e/max/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/max/dependencies.bicep index 6e0fe47d04..2accc238d1 100644 --- a/avm/res/storage/storage-account/tests/e2e/max/dependencies.bicep +++ b/avm/res/storage/storage-account/tests/e2e/max/dependencies.bicep @@ -22,7 +22,29 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'defaultSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 20, 0) + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + } + } + { + name: 'custom-private-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 20, 1) + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + } + } + { + name: 'custom-private-subnet-2' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 20, 2) serviceEndpoints: [ { service: 'Microsoft.Storage' @@ -58,8 +80,14 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018- location: location } -@description('The resource ID of the created Virtual Network Subnet.') -output subnetResourceId string = virtualNetwork.properties.subnets[0].id +@description('The resource ID of the created Virtual Network Default Subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the 2nd created Virtual Network Subnet.') +output customSubnet1ResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the 3rd created Virtual Network Subnet.') +output customSubnet2ResourceId string = virtualNetwork.properties.subnets[2].id @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep index 2b8887fa11..3773a4e2c3 100644 --- a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep @@ -80,7 +80,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' privateEndpoints: [ { service: 'blob' - subnetResourceId: nestedDependencies.outputs.subnetResourceId + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId privateDnsZoneResourceIds: [ nestedDependencies.outputs.privateDNSZoneResourceId ] @@ -90,6 +90,48 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + { + service: 'blob' + subnetResourceId: nestedDependencies.outputs.customSubnet2ResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId + service: 'table' + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId + service: 'queue' + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId + service: 'file' + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId + service: 'web' + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId + service: 'dfs' + } ] networkAcls: { resourceAccessRules: [ @@ -103,7 +145,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' virtualNetworkRules: [ { action: 'Allow' - id: nestedDependencies.outputs.subnetResourceId + id: nestedDependencies.outputs.defaultSubnetResourceId } ] ipRules: [ @@ -413,4 +455,8 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] }] diff --git a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/main.test.bicep index 936455ba72..2c2bfc3700 100644 --- a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/main.test.bicep @@ -82,4 +82,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/main.test.bicep index 505551c7aa..3997f6a7f1 100644 --- a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/main.test.bicep @@ -89,4 +89,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep index 8d3c2cb47c..0fdbb91dff 100644 --- a/avm/res/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/waf-aligned/main.test.bicep @@ -301,4 +301,8 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] }] diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index e42c3d9e5f..0f81d22abc 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/synapse/private-link-hub/README.md b/avm/res/synapse/private-link-hub/README.md index 82e2a891c3..7534fb874e 100644 --- a/avm/res/synapse/private-link-hub/README.md +++ b/avm/res/synapse/private-link-hub/README.md @@ -112,7 +112,6 @@ module privateLinkHub 'br/public:avm/res/synapse/private-link-hub:' = { privateDnsZoneResourceIds: [ '' ] - service: 'Web' subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -120,6 +119,12 @@ module privateLinkHub 'br/public:avm/res/synapse/private-link-hub:' = { Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + '' + ] + subnetResourceId: '' + } ] roleAssignments: [ { @@ -174,13 +179,18 @@ module privateLinkHub 'br/public:avm/res/synapse/private-link-hub:' = { "privateDnsZoneResourceIds": [ "" ], - "service": "Web", "subnetResourceId": "", "tags": { "Environment": "Non-Prod", "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "subnetResourceId": "" } ] }, @@ -392,7 +402,6 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | | [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | **Optional parameters** @@ -404,22 +413,17 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | +| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | -### Parameter: `privateEndpoints.service` - -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". - -- Required: Yes -- Type: string - ### Parameter: `privateEndpoints.subnetResourceId` Resource ID of the subnet where the endpoint needs to be created. @@ -445,19 +449,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -510,7 +514,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -528,11 +532,18 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + ### Parameter: `privateEndpoints.location` The location to deploy the private endpoint to. @@ -576,12 +587,12 @@ Specify the name of lock. - Required: No - Type: string -### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections` +### Parameter: `privateEndpoints.manualConnectionRequestMessage` -Manual PrivateLink Service Connections. +A message passed to the owner of the remote resource with the manual connection request. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.name` @@ -592,7 +603,7 @@ The name of the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroupName` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. - Required: No - Type: string @@ -693,6 +704,13 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `privateEndpoints.service` + +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.tags` Tags to be applied on all resources/resource groups in this deployment. @@ -812,7 +830,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.3.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | ## Data Collection diff --git a/avm/res/synapse/private-link-hub/main.bicep b/avm/res/synapse/private-link-hub/main.bicep index ea2d80db82..22d0d278ce 100644 --- a/avm/res/synapse/private-link-hub/main.bicep +++ b/avm/res/synapse/private-link-hub/main.bicep @@ -81,34 +81,45 @@ resource privateLinkHub_roleAssignments 'Microsoft.Authorization/roleAssignments }] // Private Endpoints -module privateLinkHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.1' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-privateLinkHub-PrivateEndpoint-${index}' +module privateLinkHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-PrivateLinkHub-PrivateEndpoint-${index}' params: { - privateLinkServiceConnections: [ + name: privateEndpoint.?name ?? 'pep-${last(split(privateLinkHub.id, '/'))}-${privateEndpoint.?service ?? 'web'}-${index}' + privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true ? [ { - name: name + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(privateLinkHub.id, '/'))}-${privateEndpoint.?service ?? 'web'}-${index}' properties: { privateLinkServiceId: privateLinkHub.id groupIds: [ - privateEndpoint.?service ?? 'privateLinkHubs' + privateEndpoint.?service ?? 'web' ] } } - ] - name: privateEndpoint.?name ?? 'pep-${last(split(privateLinkHub.id, '/'))}-${privateEndpoint.?service ?? privateEndpoint.service}-${index}' + ] : null + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(privateLinkHub.id, '/'))}-${privateEndpoint.?service ?? 'web'}-${index}' + properties: { + privateLinkServiceId: privateLinkHub.id + groupIds: [ + privateEndpoint.?service ?? 'web' + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] : null subnetResourceId: privateEndpoint.subnetResourceId + enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location lock: privateEndpoint.?lock ?? lock privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections customDnsConfigs: privateEndpoint.?customDnsConfigs ipConfigurations: privateEndpoint.?ipConfigurations applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName - enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry } }] @@ -166,24 +177,31 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Required. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') - service: string + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') + service: string? @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') + @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') privateDnsZoneGroupName: string? @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -200,7 +218,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? @@ -220,9 +238,6 @@ type privateEndpointType = { @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') tags: object? - @description('Optional. Manual PrivateLink Service Connections.') - manualPrivateLinkServiceConnections: array? - @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? }[]? diff --git a/avm/res/synapse/private-link-hub/main.json b/avm/res/synapse/private-link-hub/main.json index d6a37b5693..d0134ae735 100644 --- a/avm/res/synapse/private-link-hub/main.json +++ b/avm/res/synapse/private-link-hub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "17829030796536004082" + "templateHash": "16624127378809450653" }, "name": "Azure Synapse Analytics", "description": "This module deploys an Azure Synapse Analytics (Private Link Hub).", @@ -125,8 +125,9 @@ }, "service": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, "subnetResourceId": { @@ -139,7 +140,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." } }, "privateDnsZoneResourceIds": { @@ -152,6 +153,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -161,7 +177,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -170,7 +186,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -209,7 +225,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -260,13 +276,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -406,32 +415,24 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-privateLinkHub-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateLinkHub-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name'))]", - "groupIds": [ - "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'privateLinkHubs')]" - ] - } - } - ] - }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), copyIndex()))]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'web'), copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'web'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'web')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'web'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'web')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, "location": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, @@ -450,9 +451,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -464,9 +462,6 @@ }, "customNetworkInterfaceName": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - }, - "enableTelemetry": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" } }, "template": { @@ -476,8 +471,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -526,7 +521,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -604,7 +599,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -707,7 +702,7 @@ "fqdn": { "type": "string", "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -716,7 +711,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -842,7 +837,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -947,8 +942,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1059,6 +1054,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } diff --git a/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep b/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep index edcbcf4ffe..26749a1732 100644 --- a/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep +++ b/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep @@ -62,7 +62,6 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' privateDnsZoneResourceIds: [ nestedDependencies.outputs.privateDNSZoneResourceId ] - service: 'Web' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -70,6 +69,12 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } ] roleAssignments: [ { @@ -89,4 +94,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep b/avm/res/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep index f25303df35..7a5577e6b5 100644 --- a/avm/res/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/synapse/private-link-hub/tests/e2e/waf-aligned/main.test.bicep @@ -72,4 +72,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/synapse/private-link-hub/version.json b/avm/res/synapse/private-link-hub/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/synapse/private-link-hub/version.json +++ b/avm/res/synapse/private-link-hub/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index a0bab3f3c1..27de83f107 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -249,6 +249,12 @@ module site 'br/public:avm/res/web/site:' = { Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + '' + ] + subnetResourceId: '' + } ] roleAssignments: [ { @@ -443,6 +449,12 @@ module site 'br/public:avm/res/web/site:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "subnetResourceId": "" } ] }, @@ -752,6 +764,12 @@ module site 'br/public:avm/res/web/site:' = { Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + '' + ] + subnetResourceId: '' + } ] publicNetworkAccess: 'Disabled' roleAssignments: [ @@ -960,6 +978,12 @@ module site 'br/public:avm/res/web/site:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "subnetResourceId": "" } ] }, @@ -1579,14 +1603,15 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | +| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | ### Parameter: `privateEndpoints.subnetResourceId` @@ -1614,19 +1639,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -1679,7 +1704,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -1697,11 +1722,18 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + ### Parameter: `privateEndpoints.location` The location to deploy the private endpoint to. @@ -1745,12 +1777,12 @@ Specify the name of lock. - Required: No - Type: string -### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections` +### Parameter: `privateEndpoints.manualConnectionRequestMessage` -Manual PrivateLink Service Connections. +A message passed to the owner of the remote resource with the manual connection request. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.name` @@ -1761,7 +1793,7 @@ The name of the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroupName` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. - Required: No - Type: string @@ -1864,7 +1896,7 @@ The principal type of the assigned principal ID. ### Parameter: `privateEndpoints.service` -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". - Required: No - Type: string @@ -2101,7 +2133,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.3.2` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | ## Notes diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 7351a9a11f..ac945af7de 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -362,12 +362,13 @@ resource app_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01 scope: app }] -module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-app-PrivateEndpoint-${index}' +module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-App-PrivateEndpoint-${index}' params: { - privateLinkServiceConnections: [ + name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' + privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true ? [ { - name: name + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' properties: { privateLinkServiceId: app.id groupIds: [ @@ -375,8 +376,19 @@ module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' = ] } } - ] - name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' + ] : null + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' + properties: { + privateLinkServiceId: app.id + groupIds: [ + privateEndpoint.?service ?? 'sites' + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] : null subnetResourceId: privateEndpoint.subnetResourceId enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location @@ -385,7 +397,6 @@ module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' = privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections customDnsConfigs: privateEndpoint.?customDnsConfigs ipConfigurations: privateEndpoint.?ipConfigurations applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds @@ -470,24 +481,31 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Optional. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') + @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') privateDnsZoneGroupName: string? @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -504,7 +522,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? @@ -524,9 +542,6 @@ type privateEndpointType = { @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') tags: object? - @description('Optional. Manual PrivateLink Service Connections.') - manualPrivateLinkServiceConnections: array? - @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? }[]? diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 500663e7fb..14f78f43f4 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "15412423005625899099" + "templateHash": "7010125481271494880" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -150,7 +150,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, "subnetResourceId": { @@ -163,7 +163,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." } }, "privateDnsZoneResourceIds": { @@ -176,6 +176,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -185,7 +200,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -194,7 +209,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -233,7 +248,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -284,13 +299,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -1288,7 +1296,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "12905465301950647404" + "templateHash": "7766866613475753287" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -1432,7 +1440,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, "subnetResourceId": { @@ -1445,7 +1453,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." } }, "privateDnsZoneResourceIds": { @@ -1458,6 +1466,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -1467,7 +1490,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -1476,7 +1499,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -1515,7 +1538,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -1566,13 +1589,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -2689,22 +2705,11 @@ }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Web/sites', parameters('appName'))]", - "groupIds": [ - "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))]" - ] - } - } - ] - }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}-{3}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), parameters('name'), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex()))]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}-{3}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), parameters('name'), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -2729,9 +2734,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -2752,8 +2754,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -2802,7 +2804,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -3118,7 +3120,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3223,8 +3225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -3335,6 +3337,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } @@ -3618,29 +3627,18 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-app-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-App-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Web/sites', parameters('name'))]", - "groupIds": [ - "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites')]" - ] - } - } - ] - }, "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -3665,9 +3663,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -3688,8 +3683,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -3738,7 +3733,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -4054,7 +4049,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -4159,8 +4154,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -4271,6 +4266,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } diff --git a/avm/res/web/site/slot/README.md b/avm/res/web/site/slot/README.md index a8410cfcb8..b43eaeeac4 100644 --- a/avm/res/web/site/slot/README.md +++ b/avm/res/web/site/slot/README.md @@ -509,14 +509,15 @@ Configuration details for private endpoints. | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | +| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | ### Parameter: `privateEndpoints.subnetResourceId` @@ -544,19 +545,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -609,7 +610,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -627,11 +628,18 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + ### Parameter: `privateEndpoints.location` The location to deploy the private endpoint to. @@ -675,12 +683,12 @@ Specify the name of lock. - Required: No - Type: string -### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections` +### Parameter: `privateEndpoints.manualConnectionRequestMessage` -Manual PrivateLink Service Connections. +A message passed to the owner of the remote resource with the manual connection request. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.name` @@ -691,7 +699,7 @@ The name of the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroupName` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. - Required: No - Type: string @@ -794,7 +802,7 @@ The principal type of the assigned principal ID. ### Parameter: `privateEndpoints.service` -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". - Required: No - Type: string @@ -1019,7 +1027,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.3.2` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | ## Notes diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index 9c91ea7372..fb10222b00 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -295,12 +295,13 @@ resource slot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-0 scope: slot }] -module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { +module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-Slot-PrivateEndpoint-${index}' params: { - privateLinkServiceConnections: [ + name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${name}-${privateEndpoint.?service ?? 'sites-${slot.name}'}-${index}' + privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true ? [ { - name: slot.name + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites-${slot.name}'}-${index}' properties: { privateLinkServiceId: app.id // Must be set on the WebApp and not the slot groupIds: [ @@ -308,8 +309,19 @@ module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' ] } } - ] - name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${name}-${privateEndpoint.?service ?? 'sites'}-${index}' + ] : null + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites-${slot.name}'}-${index}' + properties: { + privateLinkServiceId: app.id // Must be set on the WebApp and not the slot + groupIds: [ + privateEndpoint.?service ?? 'sites-${slot.name}' // The required syntax to create the private endpoint for a specific slot + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] : null subnetResourceId: privateEndpoint.subnetResourceId enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location @@ -318,7 +330,6 @@ module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.2' privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections customDnsConfigs: privateEndpoint.?customDnsConfigs ipConfigurations: privateEndpoint.?ipConfigurations applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds @@ -391,24 +402,31 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Optional. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') + @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') privateDnsZoneGroupName: string? @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -425,7 +443,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? @@ -445,9 +463,6 @@ type privateEndpointType = { @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') tags: object? - @description('Optional. Manual PrivateLink Service Connections.') - manualPrivateLinkServiceConnections: array? - @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? }[]? diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index a4c60a5730..2a2938a707 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "12905465301950647404" + "templateHash": "7766866613475753287" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -150,7 +150,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, "subnetResourceId": { @@ -163,7 +163,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." } }, "privateDnsZoneResourceIds": { @@ -176,6 +176,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -185,7 +200,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -194,7 +209,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -233,7 +248,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -284,13 +299,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -1407,22 +1415,11 @@ }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Web/sites', parameters('appName'))]", - "groupIds": [ - "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))]" - ] - } - } - ] - }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}-{3}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), parameters('name'), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex()))]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}-{3}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), parameters('name'), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -1447,9 +1444,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -1470,8 +1464,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1520,7 +1514,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -1836,7 +1830,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1941,8 +1935,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -2053,6 +2047,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } diff --git a/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep b/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep index 0cb0a8ee87..06353e92cf 100644 --- a/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep +++ b/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep @@ -182,6 +182,12 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } ] roleAssignments: [ { diff --git a/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep b/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep index 751167f4e4..c1dba11860 100644 --- a/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep +++ b/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep @@ -182,6 +182,12 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } ] roleAssignments: [ { diff --git a/avm/res/web/site/version.json b/avm/res/web/site/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/web/site/version.json +++ b/avm/res/web/site/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/web/static-site/README.md b/avm/res/web/static-site/README.md index a4a37abdff..fd130bc092 100644 --- a/avm/res/web/static-site/README.md +++ b/avm/res/web/static-site/README.md @@ -136,6 +136,12 @@ module staticSite 'br/public:avm/res/web/static-site:' = { Role: 'DeploymentValidation' } } + { + privateDnsZoneResourceIds: [ + '' + ] + subnetResourceId: '' + } ] roleAssignments: [ { @@ -234,6 +240,12 @@ module staticSite 'br/public:avm/res/web/static-site:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } + }, + { + "privateDnsZoneResourceIds": [ + "" + ], + "subnetResourceId": "" } ] }, @@ -633,14 +645,15 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | +| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | ### Parameter: `privateEndpoints.subnetResourceId` @@ -668,19 +681,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -733,7 +746,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -751,11 +764,18 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + ### Parameter: `privateEndpoints.location` The location to deploy the private endpoint to. @@ -799,12 +819,12 @@ Specify the name of lock. - Required: No - Type: string -### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections` +### Parameter: `privateEndpoints.manualConnectionRequestMessage` -Manual PrivateLink Service Connections. +A message passed to the owner of the remote resource with the manual connection request. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.name` @@ -815,7 +835,7 @@ The name of the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroupName` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. - Required: No - Type: string @@ -918,7 +938,7 @@ The principal type of the assigned principal ID. ### Parameter: `privateEndpoints.service` -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". - Required: No - Type: string @@ -1103,7 +1123,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.3.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | ## Data Collection diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep index 3cbc8d702b..78688b9023 100644 --- a/avm/res/web/static-site/main.bicep +++ b/avm/res/web/static-site/main.bicep @@ -201,12 +201,13 @@ resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@202 scope: staticSite }] -module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.1' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-staticSite-PrivateEndpoint-${index}' +module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-StaticSite-PrivateEndpoint-${index}' params: { - privateLinkServiceConnections: [ + name: privateEndpoint.?name ?? 'pep-${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' + privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true ? [ { - name: name + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' properties: { privateLinkServiceId: staticSite.id groupIds: [ @@ -214,8 +215,19 @@ module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0 ] } } - ] - name: privateEndpoint.?name ?? 'pep-${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' + ] : null + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' + properties: { + privateLinkServiceId: staticSite.id + groupIds: [ + privateEndpoint.?service ?? 'staticSites' + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] : null subnetResourceId: privateEndpoint.subnetResourceId enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location @@ -224,7 +236,6 @@ module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0 privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections customDnsConfigs: privateEndpoint.?customDnsConfigs ipConfigurations: privateEndpoint.?ipConfigurations applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds @@ -300,24 +311,31 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Optional. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') + @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') privateDnsZoneGroupName: string? @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -334,7 +352,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? @@ -354,9 +372,6 @@ type privateEndpointType = { @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') tags: object? - @description('Optional. Manual PrivateLink Service Connections.') - manualPrivateLinkServiceConnections: array? - @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? }[]? diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json index bd76acc54e..b226d1b32c 100644 --- a/avm/res/web/static-site/main.json +++ b/avm/res/web/static-site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "6139621116295404519" + "templateHash": "15307408528388407015" }, "name": "Static Web Apps", "description": "This module deploys a Static Web App.", @@ -150,7 +150,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, "subnetResourceId": { @@ -163,7 +163,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." } }, "privateDnsZoneResourceIds": { @@ -176,6 +176,21 @@ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." } }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, "customDnsConfigs": { "type": "array", "items": { @@ -185,7 +200,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -194,7 +209,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -233,7 +248,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -284,13 +299,6 @@ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." - } - }, "enableTelemetry": { "type": "bool", "nullable": true, @@ -955,29 +963,18 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-staticSite-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-StaticSite-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateLinkServiceConnections": { - "value": [ - { - "name": "[parameters('name')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Web/staticSites', parameters('name'))]", - "groupIds": [ - "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites')]" - ] - } - } - ] - }, "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/staticSites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites'), copyIndex()))]" }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/staticSites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/staticSites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/staticSites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/staticSites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -1002,9 +999,6 @@ "tags": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "manualPrivateLinkServiceConnections": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]" - }, "customDnsConfigs": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, @@ -1025,8 +1019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "2821141217598568122" + "version": "0.24.24.22086", + "templateHash": "2592884001616184297" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1075,7 +1069,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. 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\"" + "description": "Optional. 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": { @@ -1153,7 +1147,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -1256,7 +1250,7 @@ "fqdn": { "type": "string", "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -1265,7 +1259,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -1391,7 +1385,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1496,8 +1490,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18168683629401652671" + "version": "0.24.24.22086", + "templateHash": "9321937464667207030" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1608,6 +1602,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" } } } diff --git a/avm/res/web/static-site/tests/e2e/max/main.test.bicep b/avm/res/web/static-site/tests/e2e/max/main.test.bicep index 36bea24219..4db79418e9 100644 --- a/avm/res/web/static-site/tests/e2e/max/main.test.bicep +++ b/avm/res/web/static-site/tests/e2e/max/main.test.bicep @@ -72,6 +72,12 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } ] roleAssignments: [ { @@ -115,4 +121,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep b/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep index 98025ccbc8..e95bd5db2b 100644 --- a/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep @@ -91,4 +91,7 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' Role: 'DeploymentValidation' } } + dependsOn: [ + nestedDependencies + ] }] diff --git a/avm/res/web/static-site/version.json b/avm/res/web/static-site/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/web/static-site/version.json +++ b/avm/res/web/static-site/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file