From 2a4d09182da5cf8806c88a44f6d998a85d6d94eb Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Mon, 18 Sep 2023 14:45:46 +0100 Subject: [PATCH 1/2] Added avm folder & PE draft --- .../.test/common/dependencies.bicep | 95 ++++ .../.test/common/main.test.bicep | 91 ++++ .../.test/min/dependencies.bicep | 54 ++ .../.test/min/main.test.bicep | 59 +++ avm/res/network/private-endpoint/README.md | 487 ++++++++++++++++++ avm/res/network/private-endpoint/main.bicep | 179 +++++++ avm/res/network/private-endpoint/main.json | 452 ++++++++++++++++ .../private-dns-zone-group/README.md | 50 ++ .../private-dns-zone-group/main.bicep | 57 ++ .../private-dns-zone-group/main.json | 105 ++++ .../private-dns-zone-group/version.json | 7 + avm/res/network/private-endpoint/version.json | 7 + 12 files changed, 1643 insertions(+) create mode 100644 avm/res/network/private-endpoint/.test/common/dependencies.bicep create mode 100644 avm/res/network/private-endpoint/.test/common/main.test.bicep create mode 100644 avm/res/network/private-endpoint/.test/min/dependencies.bicep create mode 100644 avm/res/network/private-endpoint/.test/min/main.test.bicep create mode 100644 avm/res/network/private-endpoint/README.md create mode 100644 avm/res/network/private-endpoint/main.bicep create mode 100644 avm/res/network/private-endpoint/main.json create mode 100644 avm/res/network/private-endpoint/private-dns-zone-group/README.md create mode 100644 avm/res/network/private-endpoint/private-dns-zone-group/main.bicep create mode 100644 avm/res/network/private-endpoint/private-dns-zone-group/main.json create mode 100644 avm/res/network/private-endpoint/private-dns-zone-group/version.json create mode 100644 avm/res/network/private-endpoint/version.json diff --git a/avm/res/network/private-endpoint/.test/common/dependencies.bicep b/avm/res/network/private-endpoint/.test/common/dependencies.bicep new file mode 100644 index 0000000000..a4bc9dabca --- /dev/null +++ b/avm/res/network/private-endpoint/.test/common/dependencies.bicep @@ -0,0 +1,95 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Application Security Group to create.') +param applicationSecurityGroupName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2023-04-01' = { + name: applicationSecurityGroupName + location: location +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@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 Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Application Security Group.') +output applicationSecurityGroupResourceId string = applicationSecurityGroup.id diff --git a/avm/res/network/private-endpoint/.test/common/main.test.bicep b/avm/res/network/private-endpoint/.test/common/main.test.bicep new file mode 100644 index 0000000000..ae3591b55b --- /dev/null +++ b/avm/res/network/private-endpoint/.test/common/main.test.bicep @@ -0,0 +1,91 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privateendpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'npecom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + applicationSecurityGroupName: 'dep-${namePrefix}-asg-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + groupIds: [ + 'vault' + ] + serviceResourceId: nestedDependencies.outputs.keyVaultResourceId + subnetResourceId: nestedDependencies.outputs.subnetResourceId + lock: 'CanNotDelete' + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + groupId: 'vault' + memberName: 'default' + privateIPAddress: '10.0.0.10' + } + } + ] + customNetworkInterfaceName: '${namePrefix}${serviceShort}001nic' + applicationSecurityGroupResourceIds: [ + nestedDependencies.outputs.applicationSecurityGroupResourceId + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/network/private-endpoint/.test/min/dependencies.bicep b/avm/res/network/private-endpoint/.test/min/dependencies.bicep new file mode 100644 index 0000000000..a2a1d93da2 --- /dev/null +++ b/avm/res/network/private-endpoint/.test/min/dependencies.bicep @@ -0,0 +1,54 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +@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 Key Vault.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/network/private-endpoint/.test/min/main.test.bicep b/avm/res/network/private-endpoint/.test/min/main.test.bicep new file mode 100644 index 0000000000..f858091d54 --- /dev/null +++ b/avm/res/network/private-endpoint/.test/min/main.test.bicep @@ -0,0 +1,59 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privateendpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'npemin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + groupIds: [ + 'vault' + ] + serviceResourceId: nestedDependencies.outputs.keyVaultResourceId + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } +} diff --git a/avm/res/network/private-endpoint/README.md b/avm/res/network/private-endpoint/README.md new file mode 100644 index 0000000000..b01d2087b3 --- /dev/null +++ b/avm/res/network/private-endpoint/README.md @@ -0,0 +1,487 @@ +# Private Endpoints `[Microsoft.Network/privateEndpoints]` + +This module deploys a Private Endpoint. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +### Resource dependency + +The following resources are required to be able to deploy this resource: + +- `PrivateDNSZone` +- `VirtualNetwork/subnet` +- The service that needs to be connected through private endpoint + +**Important**: Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `groupIds` | array | Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to. | +| `name` | string | Name of the private endpoint resource to create. | +| `serviceResourceId` | string | Resource ID of the resource that needs to be connected to the network. | +| `subnetResourceId` | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `applicationSecurityGroupResourceIds` | array | `[]` | | Application security groups in which the private endpoint IP configuration is included. | +| `customDnsConfigs` | array | `[]` | | Custom DNS configurations. | +| `customNetworkInterfaceName` | string | `''` | | The custom name of the network interface attached to the private endpoint. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ipConfigurations` | array | `[]` | | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `manualPrivateLinkServiceConnections` | array | `[]` | | Manual PrivateLink Service Connections. | +| `privateDnsZoneResourceIds` | array | `[]` | | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags to be applied on all resources/resource groups in this deployment. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +
+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `applicationSecurityGroups` + +You can attach multiple Application Security Groups to a private endpoint resource. + +

+ +Parameter JSON format + +```json +"applicationSecurityGroups": { + "value": [ + { + "id": "" + }, + { + "id": "" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +applicationSecurityGroups: [ + { + id: '' + } + { + id: '' + } +] +``` + +
+

+ +### Parameter Usage: `customNetworkInterfaceName` + +You can customize the name of the private endpoint network interface instead of the default one that contains the string 'nic.GUID'. This helps with having consistent naming standards across all resources. Existing private endpoints cannot be renamed. See [documentation](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#network-interface-rename) for more details. + +

+ +Parameter JSON format + +```json +"customNetworkInterfaceName": { + "value": "myPrivateEndpointName-Nic" +} +``` + +
+ +
+ +Bicep format + +```bicep +customNetworkInterfaceName: 'myPrivateEndpointName-Nic' +``` + +
+

+ +### Parameter Usage: `ipConfigurations` + +You can use this property to define a static IP address for the private endpoint instead of the default dynamic one. To do that, first extract the `memberName` and `groupId` for the resource type you are creating the private endpoint for. See [documentation](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#determine-groupid-and-membername) for guidance on how to do that. Also provide the `privateIPAddress` for the private endpoint from the subnet range you are creating the private endpoint in. Note that static IP addresses [can be applied](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#custom-properties) when the private endpoint is created. + +

+ +Parameter JSON format + +```json +"customNetworkInterfaceName": { + "value": [ + { + "name": "myIPconfig", + "properties": { + "memberName": "", // e.g. default, sites, blob + "groupId": "", // e.g. vault, registry, blob + "privateIPAddress": "10.10.10.10" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + memberName: '' // e.g. default, sites, blob + groupId: '' // e.g. vault, registry, blob + privateIPAddress: '10.10.10.10' + } + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private endpoint. | +| `resourceGroupName` | string | The resource group the private endpoint was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module privateEndpoint './network/private-endpoint/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npecom' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: 'npecom001' + serviceResourceId: '' + subnetResourceId: '' + // Non-required parameters + applicationSecurityGroupResourceIds: [ + '' + ] + customNetworkInterfaceName: 'npecom001nic' + enableDefaultTelemetry: '' + ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + groupId: 'vault' + memberName: 'default' + privateIPAddress: '10.0.0.10' + } + } + ] + lock: 'CanNotDelete' + privateDnsZoneResourceIds: [ + '' + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "npecom001" + }, + "serviceResourceId": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "applicationSecurityGroupResourceIds": { + "value": [ + "" + ] + }, + "customNetworkInterfaceName": { + "value": "npecom001nic" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "ipConfigurations": { + "value": [ + { + "name": "myIPconfig", + "properties": { + "groupId": "vault", + "memberName": "default", + "privateIPAddress": "10.0.0.10" + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "privateDnsZoneResourceIds": { + "value": [ + "" + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module privateEndpoint './network/private-endpoint/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npemin' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: 'npemin001' + serviceResourceId: '' + subnetResourceId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "npemin001" + }, + "serviceResourceId": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/avm/res/network/private-endpoint/main.bicep b/avm/res/network/private-endpoint/main.bicep new file mode 100644 index 0000000000..aba8c911fa --- /dev/null +++ b/avm/res/network/private-endpoint/main.bicep @@ -0,0 +1,179 @@ +metadata name = 'Private Endpoints' +metadata description = 'This module deploys a Private Endpoint.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the private endpoint resource to create.') +param name string + +@description('Required. Resource ID of the subnet where the endpoint needs to be created.') +param subnetResourceId string + +@description('Required. Resource ID of the resource that needs to be connected to the network.') +param serviceResourceId string + +@description('Optional. Application security groups in which the private endpoint IP configuration is included.') +param applicationSecurityGroupResourceIds array = [] + +@description('Optional. The custom name of the network interface attached to the private endpoint.') +param customNetworkInterfaceName string = '' + +@description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') +param ipConfigurations array = [] + +@description('Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to.') +param groupIds array + +@description('Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') +param privateDnsZoneResourceIds array = [] + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments roleAssignmentType[] = [] + +@description('Optional. Tags to be applied on all resources/resource groups in this deployment.') +param tags object = {} + +@description('Optional. Custom DNS configurations.') +param customDnsConfigs array = [] + +@description('Optional. Manual PrivateLink Service Connections.') +param manualPrivateLinkServiceConnections array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { + name: name + location: location + tags: tags + properties: { + applicationSecurityGroups: [for applicationSecurityGroupResourceId in applicationSecurityGroupResourceIds: { + id: applicationSecurityGroupResourceId + }] + customDnsConfigs: customDnsConfigs + customNetworkInterfaceName: customNetworkInterfaceName + ipConfigurations: ipConfigurations + manualPrivateLinkServiceConnections: manualPrivateLinkServiceConnections + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: serviceResourceId + groupIds: groupIds + } + } + ] + subnet: { + id: subnetResourceId + } + + } +} + +module privateEndpoint_privateDnsZoneGroup 'private-dns-zone-group/main.bicep' = if (!empty(privateDnsZoneResourceIds)) { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-PrivateDnsZoneGroup' + params: { + privateDNSResourceIds: privateDnsZoneResourceIds + privateEndpointName: privateEndpoint.name + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource privateEndpoint_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${privateEndpoint.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateEndpoint +} + +resource privateEndpoint_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in roleAssignments: { + name: guid(privateEndpoint.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description ?? null + principalType: roleAssignment.?principalType ?? null + condition: roleAssignment.?condition ?? null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId ?? null + } + scope: privateEndpoint +}] + +@description('The resource group the private endpoint was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the private endpoint.') +output resourceId string = privateEndpoint.id + +@description('The name of the private endpoint.') +output name string = privateEndpoint.name + +@description('The location the resource was deployed into.') +output location string = privateEndpoint.location + +// ================ // +// Definitions // +// ================ // + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @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"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} diff --git a/avm/res/network/private-endpoint/main.json b/avm/res/network/private-endpoint/main.json new file mode 100644 index 0000000000..49b5d93d9e --- /dev/null +++ b/avm/res/network/private-endpoint/main.json @@ -0,0 +1,452 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "4111833939868956587" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + }, + "enableReferencedModulesTelemetry": false + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(parameters('applicationSecurityGroupResourceIds'))]", + "input": { + "id": "[parameters('applicationSecurityGroupResourceIds')[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('name')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneResourceIds')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "2469208411936339153" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/README.md b/avm/res/network/private-endpoint/private-dns-zone-group/README.md new file mode 100644 index 0000000000..e1d46b8986 --- /dev/null +++ b/avm/res/network/private-endpoint/private-dns-zone-group/README.md @@ -0,0 +1,50 @@ +# Private Endpoint Private DNS Zone Groups `[Microsoft.Network/privateEndpoints/privateDnsZoneGroups]` + +This module deploys a Private Endpoint Private DNS Zone Group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDNSResourceIds` | array | Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateEndpointName` | string | The name of the parent private endpoint. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `'default'` | The name of the private DNS zone group. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the private endpoint DNS zone group. | +| `resourceGroupName` | string | The resource group the private endpoint DNS zone group was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint DNS zone group. | + +## Cross-referenced modules + +_None_ diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep b/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep new file mode 100644 index 0000000000..316f0800b6 --- /dev/null +++ b/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep @@ -0,0 +1,57 @@ +metadata name = 'Private Endpoint Private DNS Zone Groups' +metadata description = 'This module deploys a Private Endpoint Private DNS Zone Group.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment.') +param privateEndpointName string + +@description('Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones.') +@minLength(1) +@maxLength(5) +param privateDNSResourceIds array + +@description('Optional. The name of the private DNS zone group.') +param name string = 'default' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var privateDnsZoneConfigs = [for privateDNSResourceId in privateDNSResourceIds: { + name: last(split(privateDNSResourceId, '/'))! + properties: { + privateDnsZoneId: privateDNSResourceId + } +}] + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' existing = { + name: privateEndpointName +} + +resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-04-01' = { + name: name + parent: privateEndpoint + properties: { + privateDnsZoneConfigs: privateDnsZoneConfigs + } +} + +@description('The name of the private endpoint DNS zone group.') +output name string = privateDnsZoneGroup.name + +@description('The resource ID of the private endpoint DNS zone group.') +output resourceId string = privateDnsZoneGroup.id + +@description('The resource group the private endpoint DNS zone group was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/main.json b/avm/res/network/private-endpoint/private-dns-zone-group/main.json new file mode 100644 index 0000000000..ffa90e33b0 --- /dev/null +++ b/avm/res/network/private-endpoint/private-dns-zone-group/main.json @@ -0,0 +1,105 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "2469208411936339153" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/version.json b/avm/res/network/private-endpoint/private-dns-zone-group/version.json new file mode 100644 index 0000000000..04a0dd1a80 --- /dev/null +++ b/avm/res/network/private-endpoint/private-dns-zone-group/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.5", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/network/private-endpoint/version.json b/avm/res/network/private-endpoint/version.json new file mode 100644 index 0000000000..04a0dd1a80 --- /dev/null +++ b/avm/res/network/private-endpoint/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.5", + "pathFilters": [ + "./main.json" + ] +} From 52fff158d4bb1c66a3f6e56da138163c3b177dc9 Mon Sep 17 00:00:00 2001 From: AlexanderSehr Date: Mon, 18 Sep 2023 15:02:58 +0100 Subject: [PATCH 2/2] Updated modules to expected folder structure --- avm/ptn/readme.md | 1 + avm/res/cognitive-services/account/README.md | 995 +++++++++ avm/res/cognitive-services/account/main.bicep | 458 ++++ avm/res/cognitive-services/account/main.json | 1338 ++++++++++++ .../account/tests/custom.tests.ps1 | 0 .../tests/e2e/common/dependencies.bicep | 68 + .../account/tests/e2e/common/main.test.bicep | 148 ++ .../tests/e2e/encr-sys/dependencies.bicep | 68 + .../tests/e2e/encr-sys/main.test.bicep | 69 + .../account/tests/e2e/encr/dependencies.bicep | 60 + .../account/tests/e2e/encr/main.test.bicep | 72 + .../account/tests/e2e/min/main.test.bicep | 46 + .../tests/e2e/speech/dependencies.bicep | 60 + .../account/tests/e2e/speech/main.test.bicep | 81 + .../cognitive-services/account/version.json | 7 + avm/res/key-vault/vault/README.md | 968 ++++++++ .../key-vault/vault/access-policy/README.md | 103 + .../key-vault/vault/access-policy/main.bicep | 52 + .../key-vault/vault/access-policy/main.json | 97 + .../vault/access-policy/version.json | 7 + avm/res/key-vault/vault/key/README.md | 237 ++ avm/res/key-vault/vault/key/main.bicep | 164 ++ avm/res/key-vault/vault/key/main.json | 297 +++ avm/res/key-vault/vault/key/version.json | 7 + avm/res/key-vault/vault/main.bicep | 416 ++++ avm/res/key-vault/vault/main.json | 1943 +++++++++++++++++ avm/res/key-vault/vault/secret/README.md | 157 ++ avm/res/key-vault/vault/secret/main.bicep | 133 ++ avm/res/key-vault/vault/secret/main.json | 251 +++ avm/res/key-vault/vault/secret/version.json | 7 + .../key-vault/vault/tests/custom.tests.ps1 | 0 .../vault/tests/e2e/common/dependencies.bicep | 65 + .../vault/tests/e2e/common/main.test.bicep | 235 ++ .../vault/tests/e2e/min/main.test.bicep | 47 + .../vault/tests/e2e/pe/dependencies.bicep | 49 + .../vault/tests/e2e/pe/main.test.bicep | 74 + avm/res/key-vault/vault/version.json | 7 + .../private-endpoint/tests/custom.tests.ps1 | 0 .../e2e}/common/dependencies.bicep | 0 .../e2e}/common/main.test.bicep | 0 .../e2e}/min/dependencies.bicep | 0 .../{.test => tests/e2e}/min/main.test.bicep | 0 42 files changed, 8787 insertions(+) create mode 100644 avm/ptn/readme.md create mode 100644 avm/res/cognitive-services/account/README.md create mode 100644 avm/res/cognitive-services/account/main.bicep create mode 100644 avm/res/cognitive-services/account/main.json create mode 100644 avm/res/cognitive-services/account/tests/custom.tests.ps1 create mode 100644 avm/res/cognitive-services/account/tests/e2e/common/dependencies.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/common/main.test.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/encr-sys/dependencies.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/encr-sys/main.test.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/encr/dependencies.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/encr/main.test.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/min/main.test.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/speech/dependencies.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/speech/main.test.bicep create mode 100644 avm/res/cognitive-services/account/version.json create mode 100644 avm/res/key-vault/vault/README.md create mode 100644 avm/res/key-vault/vault/access-policy/README.md create mode 100644 avm/res/key-vault/vault/access-policy/main.bicep create mode 100644 avm/res/key-vault/vault/access-policy/main.json create mode 100644 avm/res/key-vault/vault/access-policy/version.json create mode 100644 avm/res/key-vault/vault/key/README.md create mode 100644 avm/res/key-vault/vault/key/main.bicep create mode 100644 avm/res/key-vault/vault/key/main.json create mode 100644 avm/res/key-vault/vault/key/version.json create mode 100644 avm/res/key-vault/vault/main.bicep create mode 100644 avm/res/key-vault/vault/main.json create mode 100644 avm/res/key-vault/vault/secret/README.md create mode 100644 avm/res/key-vault/vault/secret/main.bicep create mode 100644 avm/res/key-vault/vault/secret/main.json create mode 100644 avm/res/key-vault/vault/secret/version.json create mode 100644 avm/res/key-vault/vault/tests/custom.tests.ps1 create mode 100644 avm/res/key-vault/vault/tests/e2e/common/dependencies.bicep create mode 100644 avm/res/key-vault/vault/tests/e2e/common/main.test.bicep create mode 100644 avm/res/key-vault/vault/tests/e2e/min/main.test.bicep create mode 100644 avm/res/key-vault/vault/tests/e2e/pe/dependencies.bicep create mode 100644 avm/res/key-vault/vault/tests/e2e/pe/main.test.bicep create mode 100644 avm/res/key-vault/vault/version.json create mode 100644 avm/res/network/private-endpoint/tests/custom.tests.ps1 rename avm/res/network/private-endpoint/{.test => tests/e2e}/common/dependencies.bicep (100%) rename avm/res/network/private-endpoint/{.test => tests/e2e}/common/main.test.bicep (100%) rename avm/res/network/private-endpoint/{.test => tests/e2e}/min/dependencies.bicep (100%) rename avm/res/network/private-endpoint/{.test => tests/e2e}/min/main.test.bicep (100%) diff --git a/avm/ptn/readme.md b/avm/ptn/readme.md new file mode 100644 index 0000000000..bee8326714 --- /dev/null +++ b/avm/ptn/readme.md @@ -0,0 +1 @@ +TODO: Add patterns \ No newline at end of file diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md new file mode 100644 index 0000000000..9f037671f3 --- /dev/null +++ b/avm/res/cognitive-services/account/README.md @@ -0,0 +1,995 @@ +# Cognitive Services `[Microsoft.CognitiveServices/accounts]` + +This module deploys a Cognitive Service. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.CognitiveServices/accounts` | [2022-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2022-12-01/accounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-kind) | string | Kind of the Cognitive Services. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. | +| [`name`](#parameter-name) | string | The name of Cognitive Services account. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`customSubDomainName`](#parameter-customsubdomainname) | string | Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedFqdnList`](#parameter-allowedfqdnlist) | array | List of allowed FQDN. | +| [`apiProperties`](#parameter-apiproperties) | object | The API properties for special APIs. | +| [`customerManagedKey`](#parameter-customermanagedkey) | | The customer managed key definition. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Allow only Azure AD authentication. Should be enabled for security reasons. | +| [`dynamicThrottlingEnabled`](#parameter-dynamicthrottlingenabled) | bool | The flag to enable dynamic throttling. | +| [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`lock`](#parameter-lock) | string | Specify the type of lock. | +| [`managedIdentities`](#parameter-managedidentities) | | The managed identity definition for this resource | +| [`migrationToken`](#parameter-migrationtoken) | string | Resource migration token. | +| [`networkAcls`](#parameter-networkacls) | object | A collection of rules governing the accessibility from specific network locations. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. | +| [`restore`](#parameter-restore) | bool | Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists. | +| [`restrictOutboundNetworkAccess`](#parameter-restrictoutboundnetworkaccess) | bool | Restrict outbound network access. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| [`sku`](#parameter-sku) | string | SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`userOwnedStorage`](#parameter-userownedstorage) | array | The storage accounts for this resource. | + +### Parameter: `allowedFqdnList` + +List of allowed FQDN. +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `apiProperties` + +The API properties for special APIs. +- Required: No +- Type: object +- Default: `{object}` + +### Parameter: `customerManagedKey` + +The customer managed key definition. +- Required: Yes +- Type: + +### Parameter: `customSubDomainName` + +Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set. +- Required: No +- Type: string +- Default: `''` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `disableLocalAuth` + +Allow only Azure AD authentication. Should be enabled for security reasons. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `dynamicThrottlingEnabled` + +The flag to enable dynamic throttling. +- Required: Yes +- Type: bool +- Default: `False` + +### Parameter: `enableDefaultTelemetry` + +Enable telemetry via a Globally Unique Identifier (GUID). +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `kind` + +Kind of the Cognitive Services. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. +- Required: Yes +- Type: string +- Allowed: `[AnomalyDetector, Bing.Autosuggest.v7, Bing.CustomSearch, Bing.EntitySearch, Bing.Search.v7, Bing.SpellCheck.v7, CognitiveServices, ComputerVision, ContentModerator, CustomVision.Prediction, CustomVision.Training, Face, FormRecognizer, ImmersiveReader, Internal.AllInOne, LUIS, LUIS.Authoring, Personalizer, QnAMaker, SpeechServices, TextAnalytics, TextTranslation]` + +### Parameter: `location` + +Location for all Resources. +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +Specify the type of lock. +- Required: No +- Type: string +- Default: `''` +- Allowed: `['', CanNotDelete, ReadOnly]` + +### Parameter: `managedIdentities` + +The managed identity definition for this resource +- Required: Yes +- Type: + +### Parameter: `migrationToken` + +Resource migration token. +- Required: No +- Type: string +- Default: `''` + +### Parameter: `name` + +The name of Cognitive Services account. +- Required: Yes +- Type: string + +### Parameter: `networkAcls` + +A collection of rules governing the accessibility from specific network locations. +- Required: No +- Type: object +- Default: `{object}` + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. +- Required: No +- Type: array +- Default: `[]` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +

+ +Parameter JSON format + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + }, + "ipConfigurations":[ + { + "name": "myIPconfigTest02", + "properties": { + "groupId": "blob", + "memberName": "blob", + "privateIPAddress": "10.0.0.30" + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // Example showing all available fields + { + name: 'sxx-az-pe' // Optional: Name will be automatically generated if one is not provided here + subnetResourceId: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + privateDnsZoneGroup: { + privateDNSResourceIds: [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + customDnsConfigs: [ + { + fqdn: 'customname.test.local' + ipAddresses: [ + '10.10.10.10' + ] + } + ] + ipConfigurations:[ + { + name: 'myIPconfigTest02' + properties: { + groupId: 'blob' + memberName: 'blob' + privateIPAddress: '10.0.0.30' + } + } + ] + } + // Example showing only mandatory fields + { + subnetResourceId: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter: `publicNetworkAccess` + +Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. +- Required: No +- Type: string +- Default: `''` +- Allowed: `['', Disabled, Enabled]` + +### Parameter: `restore` + +Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists. +- Required: Yes +- Type: bool +- Default: `False` + +### Parameter: `restrictOutboundNetworkAccess` + +Restrict outbound network access. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `roleAssignments` + +Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +- Required: No +- Type: array +- Default: `[]` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter: `sku` + +SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. +- Required: No +- Type: string +- Default: `'S0'` +- Allowed: `[C2, C3, C4, F0, F1, S, S0, S1, S10, S2, S3, S4, S5, S6, S7, S8, S9]` + +### Parameter: `tags` + +Tags of the resource. +- Required: No +- Type: object +- Default: `{object}` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter: `userOwnedStorage` + +The storage accounts for this resource. +- Required: No +- Type: array +- Default: `[]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The service endpoint of the cognitive services account. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the cognitive services account. | +| `resourceGroupName` | string | The resource group the cognitive services account was deployed into. | +| `resourceId` | string | The resource ID of the cognitive services account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Considerations + +- Not all combinations of parameters `kind` and `SKU` are valid and they may vary in different Azure Regions. Please use PowerShell cmdlet `Get-AzCognitiveServicesAccountSku` or another methods to determine valid values in your region. +- Not all kinds of Cognitive Services support virtual networks. Please visit the link below to determine supported services. + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `network/private-endpoint` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module account './cognitive-services/account/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-csacom' + params: { + // Required parameters + kind: 'Face' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + '' + ] + } + name: 'csacom001' + // Non-required parameters + customSubDomainName: 'xdomain' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + category: 'RequestResponse' + } + { + category: 'Audit' + } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + networkAcls: { + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: '' + ignoreMissingVnetServiceEndpoint: false + } + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'account' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'S0' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "Face" + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourcesIds": [ + "" + ] + } + }, + "name": { + "value": "csacom001" + }, + // Non-required parameters + "customSubDomainName": { + "value": "xdomain" + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "category": "RequestResponse" + }, + { + "category": "Audit" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + }, + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "defaultAction": "Deny", + "ipRules": [ + { + "value": "40.74.28.0/23" + } + ], + "virtualNetworkRules": [ + { + "id": "", + "ignoreMissingVnetServiceEndpoint": false + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "account", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "S0" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Encr

+ +
+ +via Bicep module + +```bicep +module account './cognitive-services/account/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-csaencr' + params: { + // Required parameters + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } + kind: 'SpeechServices' + managedIdentities: { + userAssignedResourcesIds: [ + '' + ] + } + name: 'csaencr001' + // Non-required parameters + enableDefaultTelemetry: '' + publicNetworkAccess: 'Enabled' + restrictOutboundNetworkAccess: false + sku: 'S0' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, + "kind": { + "value": "SpeechServices" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "" + ] + } + }, + "name": { + "value": "csaencr001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "restrictOutboundNetworkAccess": { + "value": false + }, + "sku": { + "value": "S0" + } + } +} +``` + +
+

+ +

Example 3: Encr-Sys

+ +
+ +via Bicep module + +```bicep +module account './cognitive-services/account/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-csaecrs' + params: { + // Required parameters + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + } + kind: 'SpeechServices' + managedIdentities: { + systemAssigned: true + } + name: '' + // Non-required parameters + enableDefaultTelemetry: '' + publicNetworkAccess: 'Enabled' + restrictOutboundNetworkAccess: false + sku: 'S0' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "" + } + }, + "kind": { + "value": "SpeechServices" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "name": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "restrictOutboundNetworkAccess": { + "value": false + }, + "sku": { + "value": "S0" + } + } +} +``` + +
+

+ +

Example 4: Min

+ +
+ +via Bicep module + +```bicep +module account './cognitive-services/account/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-csamin' + params: { + // Required parameters + kind: 'SpeechServices' + name: 'csamin001' + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "SpeechServices" + }, + "name": { + "value": "csamin001" + }, + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Speech

+ +
+ +via Bicep module + +```bicep +module account './cognitive-services/account/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-csaspeech' + params: { + // Required parameters + kind: 'SpeechServices' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + '' + ] + } + name: 'csaspeech001' + // Non-required parameters + customSubDomainName: 'speechdomain' + enableDefaultTelemetry: '' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'account' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + sku: 'S0' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "SpeechServices" + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourcesIds": [ + "" + ] + } + }, + "name": { + "value": "csaspeech001" + }, + // Non-required parameters + "customSubDomainName": { + "value": "speechdomain" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "account", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "sku": { + "value": "S0" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/avm/res/cognitive-services/account/main.bicep b/avm/res/cognitive-services/account/main.bicep new file mode 100644 index 0000000000..df11f21e69 --- /dev/null +++ b/avm/res/cognitive-services/account/main.bicep @@ -0,0 +1,458 @@ +metadata name = 'Cognitive Services' +metadata description = 'This module deploys a Cognitive Service.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of Cognitive Services account.') +param name string + +@description('Required. Kind of the Cognitive Services. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.') +@allowed([ + 'AnomalyDetector' + 'Bing.Autosuggest.v7' + 'Bing.CustomSearch' + 'Bing.EntitySearch' + 'Bing.Search.v7' + 'Bing.SpellCheck.v7' + 'CognitiveServices' + 'ComputerVision' + 'ContentModerator' + 'CustomVision.Prediction' + 'CustomVision.Training' + 'Face' + 'FormRecognizer' + 'ImmersiveReader' + 'Internal.AllInOne' + 'LUIS' + 'LUIS.Authoring' + 'Personalizer' + 'QnAMaker' + 'SpeechServices' + 'TextAnalytics' + 'TextTranslation' +]) +param kind string + +@description('Optional. SKU of the Cognitive Services resource. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.') +@allowed([ + 'C2' + 'C3' + 'C4' + 'F0' + 'F1' + 'S' + 'S0' + 'S1' + 'S10' + 'S2' + 'S3' + 'S4' + 'S5' + 'S6' + 'S7' + 'S8' + 'S9' +]) +param sku string = 'S0' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType[] = [] + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Conditional. Subdomain name used for token-based authentication. Required if \'networkAcls\' or \'privateEndpoints\' are set.') +param customSubDomainName string = '' + +@description('Optional. A collection of rules governing the accessibility from specific network locations.') +param networkAcls object = {} + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointType[] = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments roleAssignmentType[] = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. List of allowed FQDN.') +param allowedFqdnList array = [] + +@description('Optional. The API properties for special APIs.') +param apiProperties object = {} + +@description('Optional. Allow only Azure AD authentication. Should be enabled for security reasons.') +param disableLocalAuth bool = true + +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType + +@description('Optional. The flag to enable dynamic throttling.') +param dynamicThrottlingEnabled bool = false + +@description('Optional. Resource migration token.') +param migrationToken string = '' + +@description('Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists.') +param restore bool = false + +@description('Optional. Restrict outbound network access.') +param restrictOutboundNetworkAccess bool = true + +@description('Optional. The storage accounts for this resource.') +param userOwnedStorage array = [] + +@description('Optional. The managed identity definition for this resource') +param managedIdentities managedIdentitiesType + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourcesIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } +var identity = !empty(managedIdentities) ? { + type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourcesIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourcesIds ?? {}) ? 'UserAssigned' : null) + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null +} : null + +var builtInRoleNames = { + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services Custom Vision Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3') + 'Cognitive Services Custom Vision Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f') + 'Cognitive Services Custom Vision Labeler': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c') + 'Cognitive Services Custom Vision Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73') + 'Cognitive Services Custom Vision Trainer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b') + 'Cognitive Services Data Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c') + 'Cognitive Services Face Recognizer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7') + 'Cognitive Services Immersive Reader User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d') + 'Cognitive Services Language Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498') + 'Cognitive Services Language Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e') + 'Cognitive Services Language Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8') + 'Cognitive Services LUIS Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8') + 'Cognitive Services LUIS Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226') + 'Cognitive Services LUIS Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27') + 'Cognitive Services Metrics Advisor Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a') + 'Cognitive Services Metrics Advisor User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8') + 'Cognitive Services OpenAI Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + 'Cognitive Services OpenAI User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') + 'Cognitive Services QnA Maker Editor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025') + 'Cognitive Services QnA Maker Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126') + 'Cognitive Services Speech Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181') + 'Cognitive Services Speech User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2022-09-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) + + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' + } +} + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + +resource cognitiveService 'Microsoft.CognitiveServices/accounts@2022-12-01' = { + name: name + kind: kind + identity: identity + location: location + tags: tags + sku: { + name: sku + } + properties: { + customSubDomainName: !empty(customSubDomainName) ? customSubDomainName : null + networkAcls: !empty(networkAcls) ? { + defaultAction: contains(networkAcls, 'defaultAction') ? networkAcls.defaultAction : null + virtualNetworkRules: contains(networkAcls, 'virtualNetworkRules') ? networkAcls.virtualNetworkRules : [] + ipRules: contains(networkAcls, 'ipRules') ? networkAcls.ipRules : [] + } : null + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + allowedFqdnList: allowedFqdnList + apiProperties: apiProperties + disableLocalAuth: disableLocalAuth + encryption: !empty(customerManagedKey) ? { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: { + identityClientId: !empty(customerManagedKey.?userAssignedIdentityResourceId ?? '') ? cMKUserAssignedIdentity.properties.clientId : null + keyVaultUri: cMKKeyVault.properties.vaultUri + keyName: customerManagedKey!.keyName + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + } + } : null + migrationToken: !empty(migrationToken) ? migrationToken : null + restore: restore + restrictOutboundNetworkAccess: restrictOutboundNetworkAccess + userOwnedStorage: !empty(userOwnedStorage) ? userOwnedStorage : null + dynamicThrottlingEnabled: dynamicThrottlingEnabled + } +} + +resource cognitiveService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${cognitiveService.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: cognitiveService +} + +resource cognitiveService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in diagnosticSettings: { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId ?? null + workspaceId: diagnosticSetting.?workspaceResourceId ?? null + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId ?? null + eventHubName: diagnosticSetting.?eventHubName ?? null + metrics: diagnosticSetting.?metricCategories ?? [ + { + category: 'AllMetrics' + timeGrain: null + enabled: true + } + ] + logs: diagnosticSetting.?logCategoriesAndGroups ?? [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId ?? null + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType ?? null + } + scope: cognitiveService +}] + +module cognitiveService_privateEndpoints '../../network/private-endpoint/main.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-CognitiveService-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: privateEndpoint.?name ?? 'pe-${last(split(cognitiveService.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: cognitiveService.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: privateEndpoint.?lock ?? lock + privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds ?? [] + roleAssignments: privateEndpoint.?roleAssignments ?? [] + tags: privateEndpoint.?tags ?? {} + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections ?? [] + customDnsConfigs: privateEndpoint.?customDnsConfigs ?? [] + ipConfigurations: privateEndpoint.?ipConfigurations ?? [] + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds ?? [] + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName ?? '' + } +}] + +resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in roleAssignments: { + name: guid(cognitiveService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description ?? null + principalType: roleAssignment.?principalType ?? null + condition: roleAssignment.?condition ?? null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId ?? null + } + scope: cognitiveService +}] + +@description('The name of the cognitive services account.') +output name string = cognitiveService.name + +@description('The resource ID of the cognitive services account.') +output resourceId string = cognitiveService.id + +@description('The resource group the cognitive services account was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The service endpoint of the cognitive services account.') +output endpoint string = cognitiveService.properties.endpoint + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = (managedIdentities.?systemAssigned ?? false) && contains(cognitiveService.identity, 'principalId') ? cognitiveService.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = cognitiveService.location + +// ================ // +// Definitions // +// ================ // + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + }[]? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics' | null)? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +} + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @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"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} + +type privateEndpointType = { + + @description('Optional. The name of the private endpoint.') + name: string? + + @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('Required. Resource ID of the subnet where the endpoint needs to be created.') + subnetResourceId: 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. Custom DNS configurations.') + customDnsConfigs: { + fqdn: string? + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + name: string + groupId: string + memberName: string + privateIpAddress: string + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: ('CanNotDelete' | 'ReadOnly' | '')? + + @description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleAssignments: roleAssignmentType[]? + + @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 telemetry via a Globally Unique Identifier (GUID).') + enableDefaultTelemetry: bool? +} + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.') + userAssignedResourcesIds: string[]? +}? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/avm/res/cognitive-services/account/main.json b/avm/res/cognitive-services/account/main.json new file mode 100644 index 0000000000..a1d7e94008 --- /dev/null +++ b/avm/res/cognitive-services/account/main.json @@ -0,0 +1,1338 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "5287717803424823578" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "privateEndpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "memberName": { + "type": "string" + }, + "privateIpAddress": { + "type": "string" + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "type": "string", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "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." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + } + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourcesIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AnomalyDetector", + "Bing.Autosuggest.v7", + "Bing.CustomSearch", + "Bing.EntitySearch", + "Bing.Search.v7", + "Bing.SpellCheck.v7", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "Personalizer", + "QnAMaker", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource" + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2022-12-01", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "customSubDomainName": "[if(not(empty(parameters('customSubDomainName'))), parameters('customSubDomainName'), null())]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), createObject('defaultAction', if(contains(parameters('networkAcls'), 'defaultAction'), parameters('networkAcls').defaultAction, null()), 'virtualNetworkRules', if(contains(parameters('networkAcls'), 'virtualNetworkRules'), parameters('networkAcls').virtualNetworkRules, createArray()), 'ipRules', if(contains(parameters('networkAcls'), 'ipRules'), parameters('networkAcls').ipRules, createArray())), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[if(not(empty(parameters('migrationToken'))), parameters('migrationToken'), null())]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[if(not(empty(parameters('userOwnedStorage'))), parameters('userOwnedStorage'), null())]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_lock": { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(parameters('diagnosticSettings'))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'storageAccountResourceId'), null())]", + "workspaceId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'workspaceResourceId'), null())]", + "eventHubAuthorizationRuleId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'eventHubAuthorizationRuleResourceId'), null())]", + "eventHubName": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'eventHubName'), null())]", + "metrics": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", + "logs": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", + "marketplacePartnerId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'marketplacePartnerResourceId'), null())]", + "logAnalyticsDestinationType": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'logAnalyticsDestinationType'), null())]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(parameters('privateEndpoints'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-CognitiveService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "groupIds": { + "value": [ + "[parameters('privateEndpoints')[copyIndex()].service]" + ] + }, + "name": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'name'), format('pe-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), parameters('privateEndpoints')[copyIndex()].service, copyIndex()))]" + }, + "serviceResourceId": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "subnetResourceId": { + "value": "[parameters('privateEndpoints')[copyIndex()].subnetResourceId]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'location'), reference(split(parameters('privateEndpoints')[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneResourceIds": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'privateDnsZoneResourceIds'), createArray())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'roleAssignments'), createArray())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'tags'), createObject())]" + }, + "manualPrivateLinkServiceConnections": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'manualPrivateLinkServiceConnections'), createArray())]" + }, + "customDnsConfigs": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'customDnsConfigs'), createArray())]" + }, + "ipConfigurations": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'ipConfigurations'), createArray())]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'applicationSecurityGroupResourceIds'), createArray())]" + }, + "customNetworkInterfaceName": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'customNetworkInterfaceName'), '')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "4111833939868956587" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + }, + "enableReferencedModulesTelemetry": false + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(parameters('applicationSecurityGroupResourceIds'))]", + "input": { + "id": "[parameters('applicationSecurityGroupResourceIds')[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('name')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneResourceIds')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "2469208411936339153" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "systemAssignedPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[if(and(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), contains(reference('cognitiveService', '2022-12-01', 'full').identity, 'principalId')), reference('cognitiveService', '2022-12-01', 'full').identity.principalId, '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2022-12-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/cognitive-services/account/tests/custom.tests.ps1 b/avm/res/cognitive-services/account/tests/custom.tests.ps1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/avm/res/cognitive-services/account/tests/e2e/common/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/common/dependencies.bicep new file mode 100644 index 0000000000..129b6f6579 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/common/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.cognitiveservices.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + 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 Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/cognitive-services/account/tests/e2e/common/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/common/main.test.bicep new file mode 100644 index 0000000000..5bd858ac7f --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/common/main.test.bicep @@ -0,0 +1,148 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csacom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + kind: 'Face' + customSubDomainName: '${namePrefix}xdomain' + diagnosticSettings: [ + { + // logAnalyticsDestinationType: + // marketplacePartnerResourceId: + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + logCategoriesAndGroups: [ + { + category: 'RequestResponse' + } + { + category: 'Audit' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + { + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + lock: 'CanNotDelete' + networkAcls: { + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: nestedDependencies.outputs.subnetResourceId + ignoreMissingVnetServiceEndpoint: false + } + ] + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: 'S0' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'account' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/cognitive-services/account/tests/e2e/encr-sys/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/encr-sys/dependencies.bicep new file mode 100644 index 0000000000..3fe7da6af3 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/encr-sys/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Cognitive Service to create.') +param cognitiveServiceName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: cognitiveServiceName + location: location + sku: { + name: 'S0' + } + identity: { + type: 'SystemAssigned' + } + kind: 'SpeechServices' + properties: { + publicNetworkAccess: 'Enabled' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by batch account + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${cognitiveService.id}-Key-Key-Vault-Crypto-User-RoleAssignment') + scope: keyVault::key + properties: { + principalId: cognitiveService.identity.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created Key Vault encryption key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Cognitive Service.') +output cognitiveServiceName string = cognitiveService.name diff --git a/avm/res/cognitive-services/account/tests/e2e/encr-sys/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/encr-sys/main.test.bicep new file mode 100644 index 0000000000..d217d793f3 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/encr-sys/main.test.bicep @@ -0,0 +1,69 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csaecrs' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + cognitiveServiceName: '${namePrefix}${serviceShort}001' + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: nestedDependencies.outputs.cognitiveServiceName + kind: 'SpeechServices' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + } + publicNetworkAccess: 'Enabled' + sku: 'S0' + managedIdentities: { + systemAssigned: true + } + restrictOutboundNetworkAccess: false + } +} diff --git a/avm/res/cognitive-services/account/tests/e2e/encr/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/encr/dependencies.bicep new file mode 100644 index 0000000000..5aefaea511 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/encr/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by batch account + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Key-Vault-Crypto-User-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created Key Vault encryption key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The client ID of the created Managed Identity.') +output managedIdentityClientId string = managedIdentity.properties.clientId diff --git a/avm/res/cognitive-services/account/tests/e2e/encr/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/encr/main.test.bicep new file mode 100644 index 0000000000..86ef62b9f4 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/encr/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csaencr' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + kind: 'SpeechServices' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } + publicNetworkAccess: 'Enabled' + sku: 'S0' + managedIdentities: { + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + restrictOutboundNetworkAccess: false + } +} diff --git a/avm/res/cognitive-services/account/tests/e2e/min/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/min/main.test.bicep new file mode 100644 index 0000000000..727b9a5a92 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/min/main.test.bicep @@ -0,0 +1,46 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csamin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + kind: 'SpeechServices' + } +} diff --git a/avm/res/cognitive-services/account/tests/e2e/speech/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/speech/dependencies.bicep new file mode 100644 index 0000000000..542150de5c --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/speech/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.cognitiveservices.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + 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 Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Private DNS zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/cognitive-services/account/tests/e2e/speech/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/speech/main.test.bicep new file mode 100644 index 0000000000..f67065d5b9 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/speech/main.test.bicep @@ -0,0 +1,81 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csaspeech' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + kind: 'SpeechServices' + customSubDomainName: '${namePrefix}speechdomain' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'account' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + sku: 'S0' + managedIdentities: { + systemAssigned: true + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/cognitive-services/account/version.json b/avm/res/cognitive-services/account/version.json new file mode 100644 index 0000000000..04a0dd1a80 --- /dev/null +++ b/avm/res/cognitive-services/account/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.5", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/key-vault/vault/README.md b/avm/res/key-vault/vault/README.md new file mode 100644 index 0000000000..c700320c9f --- /dev/null +++ b/avm/res/key-vault/vault/README.md @@ -0,0 +1,968 @@ +# Key Vaults `[Microsoft.KeyVault/vaults]` + +This module deploys a Key Vault. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | +| `Microsoft.KeyVault/vaults/keys` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/keys) | +| `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Key Vault. Must be globally unique. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessPolicies`](#parameter-accesspolicies) | array | All access policies to create. | +| [`createMode`](#parameter-createmode) | string | The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | +| [`enablePurgeProtection`](#parameter-enablepurgeprotection) | bool | Provide 'true' to enable Key Vault's purge protection feature. | +| [`enableRbacAuthorization`](#parameter-enablerbacauthorization) | bool | Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC. | +| [`enableSoftDelete`](#parameter-enablesoftdelete) | bool | Switch to enable/disable Key Vault's soft delete feature. | +| [`enableVaultForDeployment`](#parameter-enablevaultfordeployment) | bool | Specifies if the vault is enabled for deployment by script or compute. | +| [`enableVaultForDiskEncryption`](#parameter-enablevaultfordiskencryption) | bool | Specifies if the azure platform has access to the vault for enabling disk encryption scenarios. | +| [`enableVaultForTemplateDeployment`](#parameter-enablevaultfortemplatedeployment) | bool | Specifies if the vault is enabled for a template deployment. | +| [`keys`](#parameter-keys) | array | All keys to create. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | string | Specify the type of lock. | +| [`networkAcls`](#parameter-networkacls) | object | Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| [`secrets`](#parameter-secrets) | secureObject | All secrets to create. | +| [`softDeleteRetentionInDays`](#parameter-softdeleteretentionindays) | int | softDelete data retention days. It accepts >=7 and <=90. | +| [`tags`](#parameter-tags) | object | Resource tags. | +| [`vaultSku`](#parameter-vaultsku) | string | Specifies the SKU for the vault. | + +### Parameter: `accessPolicies` + +All access policies to create. +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `createMode` + +The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default. +- Required: No +- Type: string +- Default: `'default'` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `enableDefaultTelemetry` + +Enable telemetry via a Globally Unique Identifier (GUID). +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enablePurgeProtection` + +Provide 'true' to enable Key Vault's purge protection feature. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableRbacAuthorization` + +Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC. +- Required: Yes +- Type: bool +- Default: `False` + +### Parameter: `enableSoftDelete` + +Switch to enable/disable Key Vault's soft delete feature. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableVaultForDeployment` + +Specifies if the vault is enabled for deployment by script or compute. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableVaultForDiskEncryption` + +Specifies if the azure platform has access to the vault for enabling disk encryption scenarios. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableVaultForTemplateDeployment` + +Specifies if the vault is enabled for a template deployment. +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `keys` + +All keys to create. +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `location` + +Location for all resources. +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +Specify the type of lock. +- Required: No +- Type: string +- Default: `''` +- Allowed: `['', CanNotDelete, ReadOnly]` + +### Parameter: `name` + +Name of the Key Vault. Must be globally unique. +- Required: Yes +- Type: string + +### Parameter: `networkAcls` + +Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny. +- Required: No +- Type: object +- Default: `{object}` + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. +- Required: No +- Type: array +- Default: `[]` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +

+ +Parameter JSON format + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + }, + "ipConfigurations":[ + { + "name": "myIPconfigTest02", + "properties": { + "groupId": "blob", + "memberName": "blob", + "privateIPAddress": "10.0.0.30" + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // Example showing all available fields + { + name: 'sxx-az-pe' // Optional: Name will be automatically generated if one is not provided here + subnetResourceId: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + privateDnsZoneGroup: { + privateDNSResourceIds: [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + customDnsConfigs: [ + { + fqdn: 'customname.test.local' + ipAddresses: [ + '10.10.10.10' + ] + } + ] + ipConfigurations:[ + { + name: 'myIPconfigTest02' + properties: { + groupId: 'blob' + memberName: 'blob' + privateIPAddress: '10.0.0.30' + } + } + ] + } + // Example showing only mandatory fields + { + subnetResourceId: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter: `publicNetworkAccess` + +Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. +- Required: No +- Type: string +- Default: `''` +- Allowed: `['', Disabled, Enabled]` + +### Parameter: `roleAssignments` + +Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +- Required: No +- Type: array +- Default: `[]` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter: `secrets` + +All secrets to create. +- Required: No +- Type: secureObject +- Default: `{object}` + +### Parameter: `softDeleteRetentionInDays` + +softDelete data retention days. It accepts >=7 and <=90. +- Required: No +- Type: int +- Default: `90` + +### Parameter: `tags` + +Resource tags. +- Required: No +- Type: object +- Default: `{object}` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter: `vaultSku` + +Specifies the SKU for the vault. +- Required: No +- Type: string +- Default: `'premium'` +- Allowed: `[premium, standard]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the key vault. | +| `resourceGroupName` | string | The name of the resource group the key vault was created in. | +| `resourceId` | string | The resource ID of the key vault. | +| `uri` | string | The URI of the key vault. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `network/private-endpoint` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module vault './key-vault/vault/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-kvvcom' + params: { + name: 'kvvcom002' + accessPolicies: [ + { + objectId: '' + permissions: { + keys: [ + 'get' + 'list' + 'update' + ] + secrets: [ + 'all' + ] + } + tenantId: '' + } + { + objectId: '' + permissions: { + certificates: [ + 'backup' + 'create' + 'delete' + ] + secrets: [ + 'all' + ] + } + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + category: 'AzurePolicyEvaluationDetails' + } + { + category: 'AuditEvent' + } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableDefaultTelemetry: '' + enablePurgeProtection: false + enableRbacAuthorization: false + keys: [ + { + attributesExp: 1725109032 + attributesNbf: 10000 + name: 'keyName' + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + rotationPolicy: { + attributes: { + expiryTime: 'P2Y' + } + lifetimeActions: [ + { + action: { + type: 'Rotate' + } + trigger: { + timeBeforeExpiry: 'P2M' + } + } + { + action: { + type: 'Notify' + } + trigger: { + timeBeforeExpiry: 'P30D' + } + } + ] + } + } + ] + lock: 'CanNotDelete' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: '' + ignoreMissingVnetServiceEndpoint: false + } + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + service: 'vault' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + secrets: { + secureList: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + contentType: 'Something' + name: 'secretName' + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + value: 'secretValue' + } + ] + } + softDeleteRetentionInDays: 7 + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "kvvcom002" + }, + "accessPolicies": { + "value": [ + { + "objectId": "", + "permissions": { + "keys": [ + "get", + "list", + "update" + ], + "secrets": [ + "all" + ] + }, + "tenantId": "" + }, + { + "objectId": "", + "permissions": { + "certificates": [ + "backup", + "create", + "delete" + ], + "secrets": [ + "all" + ] + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "category": "AzurePolicyEvaluationDetails" + }, + { + "category": "AuditEvent" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + }, + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enablePurgeProtection": { + "value": false + }, + "enableRbacAuthorization": { + "value": false + }, + "keys": { + "value": [ + { + "attributesExp": 1725109032, + "attributesNbf": 10000, + "name": "keyName", + "roleAssignments": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "rotationPolicy": { + "attributes": { + "expiryTime": "P2Y" + }, + "lifetimeActions": [ + { + "action": { + "type": "Rotate" + }, + "trigger": { + "timeBeforeExpiry": "P2M" + } + }, + { + "action": { + "type": "Notify" + }, + "trigger": { + "timeBeforeExpiry": "P30D" + } + } + ] + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "ipRules": [ + { + "value": "40.74.28.0/23" + } + ], + "virtualNetworkRules": [ + { + "id": "", + "ignoreMissingVnetServiceEndpoint": false + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "roleAssignments": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "service": "vault", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "secrets": { + "value": { + "secureList": [ + { + "attributesExp": 1702648632, + "attributesNbf": 10000, + "contentType": "Something", + "name": "secretName", + "roleAssignments": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "value": "secretValue" + } + ] + } + }, + "softDeleteRetentionInDays": { + "value": 7 + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module vault './key-vault/vault/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-kvvmin' + params: { + // Required parameters + name: 'kvvmin002' + // Non-required parameters + enableDefaultTelemetry: '' + enablePurgeProtection: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kvvmin002" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "enablePurgeProtection": { + "value": false + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module vault './key-vault/vault/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-kvvpe' + params: { + // Required parameters + name: 'kvvpe001' + // Non-required parameters + enableDefaultTelemetry: '' + enablePurgeProtection: false + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'vault' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kvvpe001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "enablePurgeProtection": { + "value": false + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "vault", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/avm/res/key-vault/vault/access-policy/README.md b/avm/res/key-vault/vault/access-policy/README.md new file mode 100644 index 0000000000..6adabd23d8 --- /dev/null +++ b/avm/res/key-vault/vault/access-policy/README.md @@ -0,0 +1,103 @@ +# Key Vault Access Policies `[Microsoft.KeyVault/vaults/accessPolicies]` + +This module deploys a Key Vault Access Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | + +## Parameters + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `accessPolicies` | array | `[]` | An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | + + +### Parameter Usage: `accessPolicies` + +

+ +Parameter JSON format + +```json +"accessPolicies": { + "value": [ + { + "tenantId": null, // Optional + "applicationId": null, // Optional + "objectId": null, + "permissions": { + "certificates": [ + "All" + ], + "keys": [ + "All" + ], + "secrets": [ + "All" + ] + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +accessPolicies: [ + { + tenantId: null // Optional + applicationId: null // Optional + objectId: null + permissions: { + certificates: [ + 'All' + ] + keys: [ + 'All' + ] + secrets: [ + 'All' + ] + } + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the access policies assignment. | +| `resourceGroupName` | string | The name of the resource group the access policies assignment was created in. | +| `resourceId` | string | The resource ID of the access policies assignment. | + +## Cross-referenced modules + +_None_ diff --git a/avm/res/key-vault/vault/access-policy/main.bicep b/avm/res/key-vault/vault/access-policy/main.bicep new file mode 100644 index 0000000000..6eeec78ae5 --- /dev/null +++ b/avm/res/key-vault/vault/access-policy/main.bicep @@ -0,0 +1,52 @@ +metadata name = 'Key Vault Access Policies' +metadata description = 'This module deploys a Key Vault Access Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault\'s tenant ID.') +param accessPolicies array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var formattedAccessPolicies = [for accessPolicy in accessPolicies: { + applicationId: contains(accessPolicy, 'applicationId') ? accessPolicy.applicationId : '' + objectId: contains(accessPolicy, 'objectId') ? accessPolicy.objectId : '' + permissions: accessPolicy.permissions + tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource policies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + name: 'add' + parent: keyVault + properties: { + accessPolicies: formattedAccessPolicies + } +} + +@description('The name of the resource group the access policies assignment was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the access policies assignment.') +output name string = policies.name + +@description('The resource ID of the access policies assignment.') +output resourceId string = policies.id diff --git a/avm/res/key-vault/vault/access-policy/main.json b/avm/res/key-vault/vault/access-policy/main.json new file mode 100644 index 0000000000..6aab64e72c --- /dev/null +++ b/avm/res/key-vault/vault/access-policy/main.json @@ -0,0 +1,97 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "10458348557666655329" + }, + "name": "Key Vault Access Policies", + "description": "This module deploys a Key Vault Access Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "accessPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(parameters('accessPolicies'))]", + "input": { + "applicationId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].applicationId, '')]", + "objectId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].objectId, '')]", + "permissions": "[parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].tenantId, tenant().tenantId)]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the access policies assignment was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policies assignment." + }, + "value": "add" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policies assignment." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/accessPolicies', parameters('keyVaultName'), 'add')]" + } + } +} \ No newline at end of file diff --git a/avm/res/key-vault/vault/access-policy/version.json b/avm/res/key-vault/vault/access-policy/version.json new file mode 100644 index 0000000000..96236a61ba --- /dev/null +++ b/avm/res/key-vault/vault/access-policy/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.4", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/key-vault/vault/key/README.md b/avm/res/key-vault/vault/key/README.md new file mode 100644 index 0000000000..8b892d702b --- /dev/null +++ b/avm/res/key-vault/vault/key/README.md @@ -0,0 +1,237 @@ +# Key Vault Keys `[Microsoft.KeyVault/vaults/keys]` + +This module deploys a Key Vault Key. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.KeyVault/vaults/keys` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/keys) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `attributesEnabled` | bool | `True` | | Determines whether the object is enabled. | +| `attributesExp` | int | `-1` | | Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible. | +| `attributesNbf` | int | `-1` | | Not before date in seconds since 1970-01-01T00:00:00Z. | +| `curveName` | string | `'P-256'` | `[P-256, P-256K, P-384, P-521]` | The elliptic curve name. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `keyOps` | array | `[]` | `[decrypt, encrypt, import, sign, unwrapKey, verify, wrapKey]` | Array of JsonWebKeyOperation. | +| `keySize` | int | `-1` | | The key size in bits. For example: 2048, 3072, or 4096 for RSA. | +| `kty` | string | `'EC'` | `[EC, EC-HSM, RSA, RSA-HSM]` | The type of the key. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `rotationPolicy` | object | `{object}` | | Key rotation policy properties object. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `rotationPolicy` + +Configures a [auto-rotation policy](https://learn.microsoft.com/en-us/azure/key-vault/keys/how-to-configure-key-rotation) for the key. +Remarks: + +- The times should use the ISO 8601 duration format, e.g. `P1Y` (1 year), `P2M`, (2 months), `P90D` (90 days). +- The `trigger` property of `lifetimeActions` can contain one of the following properties: + - `timeAfterCreate` - The time duration after key creation to rotate the key. It only applies to rotate. + - `timeBeforeExpiry` - The time duration before key expiring to rotate or notify. To use this, the key must have an expiration date configured. + +

+ +Parameter JSON format + +```json +"rotationPolicy": { + "value": { + "attributes": { + "expiryTime": "P2Y" + }, + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P2M" + }, + "action": { + "type": "Rotate" + } + }, + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "Notify" + } + } + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +rotationPolicy: { + attributes: { + expiryTime: 'P2Y' + } + lifetimeActions: [ + { + trigger: { + timeBeforeExpiry: 'P2M' + } + action: { + type: 'Rotate' + } + } + { + trigger: { + timeBeforeExpiry: 'P30D' + } + action: { + type: 'Notify' + } + } + ] +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. | +| `resourceGroupName` | string | The name of the resource group the key was created in. | +| `resourceId` | string | The resource ID of the key. | + +## Cross-referenced modules + +_None_ diff --git a/avm/res/key-vault/vault/key/main.bicep b/avm/res/key-vault/vault/key/main.bicep new file mode 100644 index 0000000000..bfec105af3 --- /dev/null +++ b/avm/res/key-vault/vault/key/main.bicep @@ -0,0 +1,164 @@ +metadata name = 'Key Vault Keys' +metadata description = 'This module deploys a Key Vault Key.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Required. The name of the key.') +param name string + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Determines whether the object is enabled.') +param attributesEnabled bool = true + +@description('Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible.') +param attributesExp int = -1 + +@description('Optional. Not before date in seconds since 1970-01-01T00:00:00Z.') +param attributesNbf int = -1 + +@description('Optional. The elliptic curve name.') +@allowed([ + 'P-256' + 'P-256K' + 'P-384' + 'P-521' +]) +param curveName string = 'P-256' + +@description('Optional. Array of JsonWebKeyOperation.') +@allowed([ + 'decrypt' + 'encrypt' + 'import' + 'sign' + 'unwrapKey' + 'verify' + 'wrapKey' +]) +param keyOps array = [] + +@description('Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA.') +param keySize int = -1 + +@description('Optional. The type of the key.') +@allowed([ + 'EC' + 'EC-HSM' + 'RSA' + 'RSA-HSM' +]) +param kty string = 'EC' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Key rotation policy properties object.') +param rotationPolicy object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Crypto Service Encryption User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') + 'Key Vault Crypto User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Key Vault Secrets User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource key 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { + name: name + parent: keyVault + tags: tags + properties: { + attributes: { + enabled: attributesEnabled + exp: attributesExp != -1 ? attributesExp : null + nbf: attributesNbf != -1 ? attributesNbf : null + } + curveName: curveName + keyOps: keyOps + keySize: keySize != -1 ? keySize : null + kty: kty + rotationPolicy: !empty(rotationPolicy) ? rotationPolicy : null + } +} + +resource key_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in roleAssignments: { + name: guid(key.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description ?? null + principalType: roleAssignment.?principalType ?? null + condition: roleAssignment.?condition ?? null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId ?? null + } + scope: key +}] + +@description('The name of the key.') +output name string = key.name + +@description('The resource ID of the key.') +output resourceId string = key.id + +@description('The name of the resource group the key was created in.') +output resourceGroupName string = resourceGroup().name + +// ================ // +// Definitions // +// ================ // + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @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"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} diff --git a/avm/res/key-vault/vault/key/main.json b/avm/res/key-vault/vault/key/main.json new file mode 100644 index 0000000000..fc30879a24 --- /dev/null +++ b/avm/res/key-vault/vault/key/main.json @@ -0,0 +1,297 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "9549513302684884018" + }, + "name": "Key Vault Keys", + "description": "This module deploys a Key Vault Key.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether the object is enabled." + } + }, + "attributesExp": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + } + }, + "attributesNbf": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + } + }, + "curveName": { + "type": "string", + "defaultValue": "P-256", + "allowedValues": [ + "P-256", + "P-256K", + "P-384", + "P-521" + ], + "metadata": { + "description": "Optional. The elliptic curve name." + } + }, + "keyOps": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + "decrypt", + "encrypt", + "import", + "sign", + "unwrapKey", + "verify", + "wrapKey" + ], + "metadata": { + "description": "Optional. Array of JsonWebKeyOperation." + } + }, + "keySize": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA." + } + }, + "kty": { + "type": "string", + "defaultValue": "EC", + "allowedValues": [ + "EC", + "EC-HSM", + "RSA", + "RSA-HSM" + ], + "metadata": { + "description": "Optional. The type of the key." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "rotationPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Key rotation policy properties object." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "key": { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[if(not(equals(parameters('attributesExp'), -1)), parameters('attributesExp'), null())]", + "nbf": "[if(not(equals(parameters('attributesNbf'), -1)), parameters('attributesNbf'), null())]" + }, + "curveName": "[parameters('curveName')]", + "keyOps": "[parameters('keyOps')]", + "keySize": "[if(not(equals(parameters('keySize'), -1)), parameters('keySize'), null())]", + "kty": "[parameters('kty')]", + "rotationPolicy": "[if(not(empty(parameters('rotationPolicy'))), parameters('rotationPolicy'), null())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "key_roleAssignments": { + "copy": { + "name": "key_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "key" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the key." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the key was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/key-vault/vault/key/version.json b/avm/res/key-vault/vault/key/version.json new file mode 100644 index 0000000000..96236a61ba --- /dev/null +++ b/avm/res/key-vault/vault/key/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.4", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep new file mode 100644 index 0000000000..03af21b054 --- /dev/null +++ b/avm/res/key-vault/vault/main.bicep @@ -0,0 +1,416 @@ +metadata name = 'Key Vaults' +metadata description = 'This module deploys a Key Vault.' +metadata owner = 'Azure/module-maintainers' + +// ================ // +// Parameters // +// ================ // +@description('Required. Name of the Key Vault. Must be globally unique.') +@maxLength(24) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. All access policies to create.') +param accessPolicies array = [] + +@description('Optional. All secrets to create.') +@secure() +param secrets object = {} + +@description('Optional. All keys to create.') +param keys array = [] + +@description('Optional. Specifies if the vault is enabled for deployment by script or compute.') +param enableVaultForDeployment bool = true + +@description('Optional. Specifies if the vault is enabled for a template deployment.') +param enableVaultForTemplateDeployment bool = true + +@description('Optional. Specifies if the azure platform has access to the vault for enabling disk encryption scenarios.') +param enableVaultForDiskEncryption bool = true + +@description('Optional. Switch to enable/disable Key Vault\'s soft delete feature.') +param enableSoftDelete bool = true + +@description('Optional. softDelete data retention days. It accepts >=7 and <=90.') +param softDeleteRetentionInDays int = 90 + +@description('Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC.') +param enableRbacAuthorization bool = false + +@description('Optional. The vault\'s create mode to indicate whether the vault need to be recovered or not. - recover or default.') +param createMode string = 'default' + +@description('Optional. Provide \'true\' to enable Key Vault\'s purge protection feature.') +param enablePurgeProtection bool = true + +@description('Optional. Specifies the SKU for the vault.') +@allowed([ + 'premium' + 'standard' +]) +param vaultSku string = 'premium' + +@description('Optional. Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny.') +param networkAcls object = {} + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments roleAssignmentType[] = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointType[] = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType[] = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Variables // +// =========== // + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Crypto Service Encryption User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') + 'Key Vault Crypto User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Key Vault Secrets User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +var formattedAccessPolicies = [for accessPolicy in accessPolicies: { + applicationId: contains(accessPolicy, 'applicationId') ? accessPolicy.applicationId : '' + objectId: contains(accessPolicy, 'objectId') ? accessPolicy.objectId : '' + permissions: accessPolicy.permissions + tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId +}] + +var secretList = !empty(secrets) ? secrets.secureList : [] + +var enableReferencedModulesTelemetry = false + +// ============ // +// Dependencies // +// ============ // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + enabledForDeployment: enableVaultForDeployment + enabledForTemplateDeployment: enableVaultForTemplateDeployment + enabledForDiskEncryption: enableVaultForDiskEncryption + enableSoftDelete: enableSoftDelete + softDeleteRetentionInDays: softDeleteRetentionInDays + enableRbacAuthorization: enableRbacAuthorization + createMode: createMode + enablePurgeProtection: enablePurgeProtection ? enablePurgeProtection : null + tenantId: subscription().tenantId + accessPolicies: formattedAccessPolicies + sku: { + name: vaultSku + family: 'A' + } + networkAcls: !empty(networkAcls) ? { + bypass: contains(networkAcls, 'bypass') ? networkAcls.bypass : null + defaultAction: contains(networkAcls, 'defaultAction') ? networkAcls.defaultAction : null + virtualNetworkRules: contains(networkAcls, 'virtualNetworkRules') ? networkAcls.virtualNetworkRules : [] + ipRules: contains(networkAcls, 'ipRules') ? networkAcls.ipRules : [] + } : null + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + } +} + +resource keyVault_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${keyVault.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: keyVault +} + +resource keyVault_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in diagnosticSettings: { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId ?? null + workspaceId: diagnosticSetting.?workspaceResourceId ?? null + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId ?? null + eventHubName: diagnosticSetting.?eventHubName ?? null + metrics: diagnosticSetting.?metricCategories ?? [ + { + category: 'AllMetrics' + timeGrain: null + enabled: true + } + ] + logs: diagnosticSetting.?logCategoriesAndGroups ?? [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId ?? null + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType ?? null + } + scope: keyVault +}] + +module keyVault_accessPolicies 'access-policy/main.bicep' = if (!empty(accessPolicies)) { + name: '${uniqueString(deployment().name, location)}-KeyVault-AccessPolicies' + params: { + keyVaultName: keyVault.name + accessPolicies: formattedAccessPolicies + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module keyVault_secrets 'secret/main.bicep' = [for (secret, index) in secretList: { + name: '${uniqueString(deployment().name, location)}-KeyVault-Secret-${index}' + params: { + name: secret.name + value: secret.value + keyVaultName: keyVault.name + attributesEnabled: contains(secret, 'attributesEnabled') ? secret.attributesEnabled : true + attributesExp: contains(secret, 'attributesExp') ? secret.attributesExp : -1 + attributesNbf: contains(secret, 'attributesNbf') ? secret.attributesNbf : -1 + contentType: contains(secret, 'contentType') ? secret.contentType : '' + tags: contains(secret, 'tags') ? secret.tags : {} + roleAssignments: contains(secret, 'roleAssignments') ? secret.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module keyVault_keys 'key/main.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-KeyVault-Key-${index}' + params: { + name: key.name + keyVaultName: keyVault.name + attributesEnabled: contains(key, 'attributesEnabled') ? key.attributesEnabled : true + attributesExp: contains(key, 'attributesExp') ? key.attributesExp : -1 + attributesNbf: contains(key, 'attributesNbf') ? key.attributesNbf : -1 + curveName: contains(key, 'curveName') ? key.curveName : 'P-256' + keyOps: contains(key, 'keyOps') ? key.keyOps : [] + keySize: contains(key, 'keySize') ? key.keySize : -1 + kty: contains(key, 'kty') ? key.kty : 'EC' + tags: contains(key, 'tags') ? key.tags : {} + roleAssignments: contains(key, 'roleAssignments') ? key.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + rotationPolicy: contains(key, 'rotationPolicy') ? key.rotationPolicy : {} + } +}] + +module keyVault_privateEndpoints '../../network/private-endpoint/main.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-KeyVault-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: privateEndpoint.?name ?? 'pe-${last(split(keyVault.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: keyVault.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: privateEndpoint.?lock ?? lock + privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds ?? [] + roleAssignments: privateEndpoint.?roleAssignments ?? [] + tags: privateEndpoint.?tags ?? {} + manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections ?? [] + customDnsConfigs: privateEndpoint.?customDnsConfigs ?? [] + ipConfigurations: privateEndpoint.?ipConfigurations ?? [] + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds ?? [] + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName ?? '' + } +}] + +resource keyVault_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in roleAssignments: { + name: guid(keyVault.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description ?? null + principalType: roleAssignment.?principalType ?? null + condition: roleAssignment.?condition ?? null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId ?? null + } + scope: keyVault +}] + +// =========== // +// Outputs // +// =========== // +@description('The resource ID of the key vault.') +output resourceId string = keyVault.id + +@description('The name of the resource group the key vault was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the key vault.') +output name string = keyVault.name + +@description('The URI of the key vault.') +output uri string = keyVault.properties.vaultUri + +@description('The location the resource was deployed into.') +output location string = keyVault.location + +// ================ // +// Definitions // +// ================ // + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + }[]? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics' | null)? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +} + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @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"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} + +type privateEndpointType = { + + @description('Optional. The name of the private endpoint.') + name: string? + + @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('Required. Resource ID of the subnet where the endpoint needs to be created.') + subnetResourceId: 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. Custom DNS configurations.') + customDnsConfigs: { + fqdn: string? + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + name: string + groupId: string + memberName: string + privateIpAddress: string + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: ('CanNotDelete' | 'ReadOnly' | '')? + + @description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleAssignments: roleAssignmentType[]? + + @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 telemetry via a Globally Unique Identifier (GUID).') + enableDefaultTelemetry: bool? +} diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json new file mode 100644 index 0000000000..1879f1333c --- /dev/null +++ b/avm/res/key-vault/vault/main.json @@ -0,0 +1,1943 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "16375955654704515617" + }, + "name": "Key Vaults", + "description": "This module deploys a Key Vault.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "privateEndpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "memberName": { + "type": "string" + }, + "privateIpAddress": { + "type": "string" + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "type": "string", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "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." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Key Vault. Must be globally unique." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "accessPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. All access policies to create." + } + }, + "secrets": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. All secrets to create." + } + }, + "keys": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. All keys to create." + } + }, + "enableVaultForDeployment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the vault is enabled for deployment by script or compute." + } + }, + "enableVaultForTemplateDeployment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the vault is enabled for a template deployment." + } + }, + "enableVaultForDiskEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the azure platform has access to the vault for enabling disk encryption scenarios." + } + }, + "enableSoftDelete": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Switch to enable/disable Key Vault's soft delete feature." + } + }, + "softDeleteRetentionInDays": { + "type": "int", + "defaultValue": 90, + "metadata": { + "description": "Optional. softDelete data retention days. It accepts >=7 and <=90." + } + }, + "enableRbacAuthorization": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC." + } + }, + "createMode": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default." + } + }, + "enablePurgeProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Provide 'true' to enable Key Vault's purge protection feature." + } + }, + "vaultSku": { + "type": "string", + "defaultValue": "premium", + "allowedValues": [ + "premium", + "standard" + ], + "metadata": { + "description": "Optional. Specifies the SKU for the vault." + } + }, + "networkAcls": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(parameters('accessPolicies'))]", + "input": { + "applicationId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].applicationId, '')]", + "objectId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].objectId, '')]", + "permissions": "[parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].tenantId, tenant().tenantId)]" + } + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "secretList": "[if(not(empty(parameters('secrets'))), parameters('secrets').secureList, createArray())]", + "enableReferencedModulesTelemetry": false + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "keyVault": { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "enabledForDeployment": "[parameters('enableVaultForDeployment')]", + "enabledForTemplateDeployment": "[parameters('enableVaultForTemplateDeployment')]", + "enabledForDiskEncryption": "[parameters('enableVaultForDiskEncryption')]", + "enableSoftDelete": "[parameters('enableSoftDelete')]", + "softDeleteRetentionInDays": "[parameters('softDeleteRetentionInDays')]", + "enableRbacAuthorization": "[parameters('enableRbacAuthorization')]", + "createMode": "[parameters('createMode')]", + "enablePurgeProtection": "[if(parameters('enablePurgeProtection'), parameters('enablePurgeProtection'), null())]", + "tenantId": "[subscription().tenantId]", + "accessPolicies": "[variables('formattedAccessPolicies')]", + "sku": { + "name": "[parameters('vaultSku')]", + "family": "A" + }, + "networkAcls": "[if(not(empty(parameters('networkAcls'))), createObject('bypass', if(contains(parameters('networkAcls'), 'bypass'), parameters('networkAcls').bypass, null()), 'defaultAction', if(contains(parameters('networkAcls'), 'defaultAction'), parameters('networkAcls').defaultAction, null()), 'virtualNetworkRules', if(contains(parameters('networkAcls'), 'virtualNetworkRules'), parameters('networkAcls').virtualNetworkRules, createArray()), 'ipRules', if(contains(parameters('networkAcls'), 'ipRules'), parameters('networkAcls').ipRules, createArray())), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]" + } + }, + "keyVault_lock": { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_diagnosticSettings": { + "copy": { + "name": "keyVault_diagnosticSettings", + "count": "[length(parameters('diagnosticSettings'))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'storageAccountResourceId'), null())]", + "workspaceId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'workspaceResourceId'), null())]", + "eventHubAuthorizationRuleId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'eventHubAuthorizationRuleResourceId'), null())]", + "eventHubName": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'eventHubName'), null())]", + "metrics": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", + "logs": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", + "marketplacePartnerId": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'marketplacePartnerResourceId'), null())]", + "logAnalyticsDestinationType": "[coalesce(tryGet(parameters('diagnosticSettings')[copyIndex()], 'logAnalyticsDestinationType'), null())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_roleAssignments": { + "copy": { + "name": "keyVault_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_accessPolicies": { + "condition": "[not(empty(parameters('accessPolicies')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-AccessPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('name')]" + }, + "accessPolicies": { + "value": "[variables('formattedAccessPolicies')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "10458348557666655329" + }, + "name": "Key Vault Access Policies", + "description": "This module deploys a Key Vault Access Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "accessPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(parameters('accessPolicies'))]", + "input": { + "applicationId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].applicationId, '')]", + "objectId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].objectId, '')]", + "permissions": "[parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].tenantId, tenant().tenantId)]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the access policies assignment was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policies assignment." + }, + "value": "add" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policies assignment." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/accessPolicies', parameters('keyVaultName'), 'add')]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_secrets": { + "copy": { + "name": "keyVault_secrets", + "count": "[length(variables('secretList'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-Secret-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('secretList')[copyIndex()].name]" + }, + "value": { + "value": "[variables('secretList')[copyIndex()].value]" + }, + "keyVaultName": { + "value": "[parameters('name')]" + }, + "attributesEnabled": "[if(contains(variables('secretList')[copyIndex()], 'attributesEnabled'), createObject('value', variables('secretList')[copyIndex()].attributesEnabled), createObject('value', true()))]", + "attributesExp": "[if(contains(variables('secretList')[copyIndex()], 'attributesExp'), createObject('value', variables('secretList')[copyIndex()].attributesExp), createObject('value', -1))]", + "attributesNbf": "[if(contains(variables('secretList')[copyIndex()], 'attributesNbf'), createObject('value', variables('secretList')[copyIndex()].attributesNbf), createObject('value', -1))]", + "contentType": "[if(contains(variables('secretList')[copyIndex()], 'contentType'), createObject('value', variables('secretList')[copyIndex()].contentType), createObject('value', ''))]", + "tags": "[if(contains(variables('secretList')[copyIndex()], 'tags'), createObject('value', variables('secretList')[copyIndex()].tags), createObject('value', createObject()))]", + "roleAssignments": "[if(contains(variables('secretList')[copyIndex()], 'roleAssignments'), createObject('value', variables('secretList')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "10247539701924243788" + }, + "name": "Key Vault Secrets", + "description": "This module deploys a Key Vault Secret.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether the object is enabled." + } + }, + "attributesExp": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + } + }, + "attributesNbf": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + } + }, + "contentType": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. The content type of the secret." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secret": { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "contentType": "[parameters('contentType')]", + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[if(not(equals(parameters('attributesExp'), -1)), parameters('attributesExp'), null())]", + "nbf": "[if(not(equals(parameters('attributesNbf'), -1)), parameters('attributesNbf'), null())]" + }, + "value": "[parameters('value')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "secretVault_roleAssignments": { + "copy": { + "name": "secretVault_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "secret" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secret." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secret." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_keys": { + "copy": { + "name": "keyVault_keys", + "count": "[length(parameters('keys'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('keys')[copyIndex()].name]" + }, + "keyVaultName": { + "value": "[parameters('name')]" + }, + "attributesEnabled": "[if(contains(parameters('keys')[copyIndex()], 'attributesEnabled'), createObject('value', parameters('keys')[copyIndex()].attributesEnabled), createObject('value', true()))]", + "attributesExp": "[if(contains(parameters('keys')[copyIndex()], 'attributesExp'), createObject('value', parameters('keys')[copyIndex()].attributesExp), createObject('value', -1))]", + "attributesNbf": "[if(contains(parameters('keys')[copyIndex()], 'attributesNbf'), createObject('value', parameters('keys')[copyIndex()].attributesNbf), createObject('value', -1))]", + "curveName": "[if(contains(parameters('keys')[copyIndex()], 'curveName'), createObject('value', parameters('keys')[copyIndex()].curveName), createObject('value', 'P-256'))]", + "keyOps": "[if(contains(parameters('keys')[copyIndex()], 'keyOps'), createObject('value', parameters('keys')[copyIndex()].keyOps), createObject('value', createArray()))]", + "keySize": "[if(contains(parameters('keys')[copyIndex()], 'keySize'), createObject('value', parameters('keys')[copyIndex()].keySize), createObject('value', -1))]", + "kty": "[if(contains(parameters('keys')[copyIndex()], 'kty'), createObject('value', parameters('keys')[copyIndex()].kty), createObject('value', 'EC'))]", + "tags": "[if(contains(parameters('keys')[copyIndex()], 'tags'), createObject('value', parameters('keys')[copyIndex()].tags), createObject('value', createObject()))]", + "roleAssignments": "[if(contains(parameters('keys')[copyIndex()], 'roleAssignments'), createObject('value', parameters('keys')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "rotationPolicy": "[if(contains(parameters('keys')[copyIndex()], 'rotationPolicy'), createObject('value', parameters('keys')[copyIndex()].rotationPolicy), createObject('value', createObject()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "9549513302684884018" + }, + "name": "Key Vault Keys", + "description": "This module deploys a Key Vault Key.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether the object is enabled." + } + }, + "attributesExp": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + } + }, + "attributesNbf": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + } + }, + "curveName": { + "type": "string", + "defaultValue": "P-256", + "allowedValues": [ + "P-256", + "P-256K", + "P-384", + "P-521" + ], + "metadata": { + "description": "Optional. The elliptic curve name." + } + }, + "keyOps": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + "decrypt", + "encrypt", + "import", + "sign", + "unwrapKey", + "verify", + "wrapKey" + ], + "metadata": { + "description": "Optional. Array of JsonWebKeyOperation." + } + }, + "keySize": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA." + } + }, + "kty": { + "type": "string", + "defaultValue": "EC", + "allowedValues": [ + "EC", + "EC-HSM", + "RSA", + "RSA-HSM" + ], + "metadata": { + "description": "Optional. The type of the key." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "rotationPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Key rotation policy properties object." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "key": { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[if(not(equals(parameters('attributesExp'), -1)), parameters('attributesExp'), null())]", + "nbf": "[if(not(equals(parameters('attributesNbf'), -1)), parameters('attributesNbf'), null())]" + }, + "curveName": "[parameters('curveName')]", + "keyOps": "[parameters('keyOps')]", + "keySize": "[if(not(equals(parameters('keySize'), -1)), parameters('keySize'), null())]", + "kty": "[parameters('kty')]", + "rotationPolicy": "[if(not(empty(parameters('rotationPolicy'))), parameters('rotationPolicy'), null())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "key_roleAssignments": { + "copy": { + "name": "key_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "key" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the key." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the key was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_privateEndpoints": { + "copy": { + "name": "keyVault_privateEndpoints", + "count": "[length(parameters('privateEndpoints'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "groupIds": { + "value": [ + "[parameters('privateEndpoints')[copyIndex()].service]" + ] + }, + "name": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'name'), format('pe-{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), parameters('privateEndpoints')[copyIndex()].service, copyIndex()))]" + }, + "serviceResourceId": { + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]" + }, + "subnetResourceId": { + "value": "[parameters('privateEndpoints')[copyIndex()].subnetResourceId]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'location'), reference(split(parameters('privateEndpoints')[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneResourceIds": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'privateDnsZoneResourceIds'), createArray())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'roleAssignments'), createArray())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'tags'), createObject())]" + }, + "manualPrivateLinkServiceConnections": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'manualPrivateLinkServiceConnections'), createArray())]" + }, + "customDnsConfigs": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'customDnsConfigs'), createArray())]" + }, + "ipConfigurations": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'ipConfigurations'), createArray())]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'applicationSecurityGroupResourceIds'), createArray())]" + }, + "customNetworkInterfaceName": { + "value": "[coalesce(tryGet(parameters('privateEndpoints')[copyIndex()], 'customNetworkInterfaceName'), '')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "4111833939868956587" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + }, + "enableReferencedModulesTelemetry": false + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(parameters('applicationSecurityGroupResourceIds'))]", + "input": { + "id": "[parameters('applicationSecurityGroupResourceIds')[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('name')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneResourceIds')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "2469208411936339153" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the key vault was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the key vault." + }, + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The URI of the key vault." + }, + "value": "[reference('keyVault').vaultUri]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('keyVault', '2022-07-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/key-vault/vault/secret/README.md b/avm/res/key-vault/vault/secret/README.md new file mode 100644 index 0000000000..bb5b627773 --- /dev/null +++ b/avm/res/key-vault/vault/secret/README.md @@ -0,0 +1,157 @@ +# Key Vault Secrets `[Microsoft.KeyVault/vaults/secrets]` + +This module deploys a Key Vault Secret. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secret. | +| `value` | securestring | The value of the secret. NOTE: "value" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `attributesEnabled` | bool | `True` | Determines whether the object is enabled. | +| `attributesExp` | int | `-1` | Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible. | +| `attributesNbf` | int | `-1` | Not before date in seconds since 1970-01-01T00:00:00Z. | +| `contentType` | securestring | `''` | The content type of the secret. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secret. | +| `resourceGroupName` | string | The name of the resource group the secret was created in. | +| `resourceId` | string | The resource ID of the secret. | + +## Cross-referenced modules + +_None_ diff --git a/avm/res/key-vault/vault/secret/main.bicep b/avm/res/key-vault/vault/secret/main.bicep new file mode 100644 index 0000000000..8ce2c1571b --- /dev/null +++ b/avm/res/key-vault/vault/secret/main.bicep @@ -0,0 +1,133 @@ +metadata name = 'Key Vault Secrets' +metadata description = 'This module deploys a Key Vault Secret.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Required. The name of the secret.') +param name string + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Determines whether the object is enabled.') +param attributesEnabled bool = true + +@description('Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible.') +param attributesExp int = -1 + +@description('Optional. Not before date in seconds since 1970-01-01T00:00:00Z.') +param attributesNbf int = -1 + +@description('Optional. The content type of the secret.') +@secure() +param contentType string = '' + +@description('Required. The value of the secret. NOTE: "value" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets.') +@secure() +param value string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Crypto Service Encryption User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') + 'Key Vault Crypto User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Key Vault Secrets User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource secret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + parent: keyVault + tags: tags + properties: { + contentType: contentType + attributes: { + enabled: attributesEnabled + exp: attributesExp != -1 ? attributesExp : null + nbf: attributesNbf != -1 ? attributesNbf : null + } + value: value + } +} + +resource secretVault_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in roleAssignments: { + name: guid(secret.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description ?? null + principalType: roleAssignment.?principalType ?? null + condition: roleAssignment.?condition ?? null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId ?? null + } + scope: secret +}] + +@description('The name of the secret.') +output name string = secret.name + +@description('The resource ID of the secret.') +output resourceId string = secret.id + +@description('The name of the resource group the secret was created in.') +output resourceGroupName string = resourceGroup().name + +// ================ // +// Definitions // +// ================ // + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @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"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} diff --git a/avm/res/key-vault/vault/secret/main.json b/avm/res/key-vault/vault/secret/main.json new file mode 100644 index 0000000000..a2ea49b35f --- /dev/null +++ b/avm/res/key-vault/vault/secret/main.json @@ -0,0 +1,251 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.21.1.54444", + "templateHash": "10247539701924243788" + }, + "name": "Key Vault Secrets", + "description": "This module deploys a Key Vault Secret.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "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\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether the object is enabled." + } + }, + "attributesExp": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + } + }, + "attributesNbf": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + } + }, + "contentType": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. The content type of the secret." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secret": { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "contentType": "[parameters('contentType')]", + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[if(not(equals(parameters('attributesExp'), -1)), parameters('attributesExp'), null())]", + "nbf": "[if(not(equals(parameters('attributesNbf'), -1)), parameters('attributesNbf'), null())]" + }, + "value": "[parameters('value')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "secretVault_roleAssignments": { + "copy": { + "name": "secretVault_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), parameters('roleAssignments')[copyIndex()].principalId, parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName], parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[parameters('roleAssignments')[copyIndex()].principalId]", + "description": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'description'), null())]", + "principalType": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'principalType'), null())]", + "condition": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'), null())]", + "conditionVersion": "[if(not(empty(tryGet(parameters('roleAssignments')[copyIndex()], 'condition'))), coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[coalesce(tryGet(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), null())]" + }, + "dependsOn": [ + "secret" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secret." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secret." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/key-vault/vault/secret/version.json b/avm/res/key-vault/vault/secret/version.json new file mode 100644 index 0000000000..96236a61ba --- /dev/null +++ b/avm/res/key-vault/vault/secret/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.4", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/key-vault/vault/tests/custom.tests.ps1 b/avm/res/key-vault/vault/tests/custom.tests.ps1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/avm/res/key-vault/vault/tests/e2e/common/dependencies.bicep b/avm/res/key-vault/vault/tests/e2e/common/dependencies.bicep new file mode 100644 index 0000000000..f433490224 --- /dev/null +++ b/avm/res/key-vault/vault/tests/e2e/common/dependencies.bicep @@ -0,0 +1,65 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSResourceId string = privateDNSZone.id diff --git a/avm/res/key-vault/vault/tests/e2e/common/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/common/main.test.bicep new file mode 100644 index 0000000000..5a737e3b20 --- /dev/null +++ b/avm/res/key-vault/vault/tests/e2e/common/main.test.bicep @@ -0,0 +1,235 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kvvcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}03' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}01' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}01' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}002' + accessPolicies: [ + { + objectId: nestedDependencies.outputs.managedIdentityPrincipalId + permissions: { + keys: [ + 'get' + 'list' + 'update' + ] + secrets: [ + 'all' + ] + } + tenantId: tenant().tenantId + } + { + objectId: nestedDependencies.outputs.managedIdentityPrincipalId + permissions: { + certificates: [ + 'backup' + 'create' + 'delete' + ] + secrets: [ + 'all' + ] + } + } + ] + diagnosticSettings: [ + { + // logAnalyticsDestinationType: + // marketplacePartnerResourceId: + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + logCategoriesAndGroups: [ + { + category: 'AzurePolicyEvaluationDetails' + } + { + category: 'AuditEvent' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + { + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + // Only for testing purposes + enablePurgeProtection: false + enableRbacAuthorization: false + keys: [ + { + attributesExp: 1725109032 + attributesNbf: 10000 + name: 'keyName' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + rotationPolicy: { + attributes: { + expiryTime: 'P2Y' + } + lifetimeActions: [ + { + trigger: { + timeBeforeExpiry: 'P2M' + } + action: { + type: 'Rotate' + } + } + { + trigger: { + timeBeforeExpiry: 'P30D' + } + action: { + type: 'Notify' + } + } + ] + } + } + ] + lock: 'CanNotDelete' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + id: nestedDependencies.outputs.subnetResourceId + ignoreMissingVnetServiceEndpoint: false + } + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSResourceId + ] + service: 'vault' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + secrets: { + secureList: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + contentType: 'Something' + name: 'secretName' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + value: 'secretValue' + } + ] + } + softDeleteRetentionInDays: 7 + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/key-vault/vault/tests/e2e/min/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/min/main.test.bicep new file mode 100644 index 0000000000..0ecea959ed --- /dev/null +++ b/avm/res/key-vault/vault/tests/e2e/min/main.test.bicep @@ -0,0 +1,47 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kvvmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}002' + // Only for testing purposes + enablePurgeProtection: false + } +} diff --git a/avm/res/key-vault/vault/tests/e2e/pe/dependencies.bicep b/avm/res/key-vault/vault/tests/e2e/pe/dependencies.bicep new file mode 100644 index 0000000000..4e44ac0dc4 --- /dev/null +++ b/avm/res/key-vault/vault/tests/e2e/pe/dependencies.bicep @@ -0,0 +1,49 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@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 Private DNS Zone.') +output privateDNSResourceId string = privateDNSZone.id diff --git a/avm/res/key-vault/vault/tests/e2e/pe/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/pe/main.test.bicep new file mode 100644 index 0000000000..d64d7cf5d2 --- /dev/null +++ b/avm/res/key-vault/vault/tests/e2e/pe/main.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kvvpe' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '[[namePrefix]]' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}${serviceShort}001' + // Only for testing purposes + enablePurgeProtection: false + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSResourceId + ] + service: 'vault' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/key-vault/vault/version.json b/avm/res/key-vault/vault/version.json new file mode 100644 index 0000000000..04a0dd1a80 --- /dev/null +++ b/avm/res/key-vault/vault/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.5", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/network/private-endpoint/tests/custom.tests.ps1 b/avm/res/network/private-endpoint/tests/custom.tests.ps1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/avm/res/network/private-endpoint/.test/common/dependencies.bicep b/avm/res/network/private-endpoint/tests/e2e/common/dependencies.bicep similarity index 100% rename from avm/res/network/private-endpoint/.test/common/dependencies.bicep rename to avm/res/network/private-endpoint/tests/e2e/common/dependencies.bicep diff --git a/avm/res/network/private-endpoint/.test/common/main.test.bicep b/avm/res/network/private-endpoint/tests/e2e/common/main.test.bicep similarity index 100% rename from avm/res/network/private-endpoint/.test/common/main.test.bicep rename to avm/res/network/private-endpoint/tests/e2e/common/main.test.bicep diff --git a/avm/res/network/private-endpoint/.test/min/dependencies.bicep b/avm/res/network/private-endpoint/tests/e2e/min/dependencies.bicep similarity index 100% rename from avm/res/network/private-endpoint/.test/min/dependencies.bicep rename to avm/res/network/private-endpoint/tests/e2e/min/dependencies.bicep diff --git a/avm/res/network/private-endpoint/.test/min/main.test.bicep b/avm/res/network/private-endpoint/tests/e2e/min/main.test.bicep similarity index 100% rename from avm/res/network/private-endpoint/.test/min/main.test.bicep rename to avm/res/network/private-endpoint/tests/e2e/min/main.test.bicep