Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add key vault to cognitive service - avm/res/cognitive-services/account #1932

138 changes: 126 additions & 12 deletions avm/res/cognitive-services/account/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This module deploys a Cognitive Service.
| `Microsoft.CognitiveServices/accounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts) |
| `Microsoft.CognitiveServices/accounts/deployments` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts/deployments) |
| `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/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) |

Expand All @@ -32,12 +33,13 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br/public:avm/res/cognitive-services/account:<version>`.

- [Using `deployments` in parameter set](#example-1-using-deployments-in-parameter-set)
- [Using only defaults](#example-2-using-only-defaults)
- [Using large parameter set](#example-3-using-large-parameter-set)
- [As Speech Service](#example-4-as-speech-service)
- [Using Customer-Managed-Keys with System-Assigned identity](#example-5-using-customer-managed-keys-with-system-assigned-identity)
- [Using Customer-Managed-Keys with User-Assigned identity](#example-6-using-customer-managed-keys-with-user-assigned-identity)
- [WAF-aligned](#example-7-waf-aligned)
- [Using defaults with key vault](#example-2-using-defaults-with-key-vault)
- [Using only defaults](#example-3-using-only-defaults)
- [Using large parameter set](#example-4-using-large-parameter-set)
- [As Speech Service](#example-5-as-speech-service)
- [Using Customer-Managed-Keys with System-Assigned identity](#example-6-using-customer-managed-keys-with-system-assigned-identity)
- [Using Customer-Managed-Keys with User-Assigned identity](#example-7-using-customer-managed-keys-with-user-assigned-identity)
- [WAF-aligned](#example-8-waf-aligned)

### Example 1: _Using `deployments` in parameter set_

Expand Down Expand Up @@ -131,7 +133,69 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 2: _Using only defaults_
### Example 2: _Using defaults with key vault_

This instance deploys the module with the minimum set of required parameters and stores the keys in key vault.


<details>

<summary>via Bicep module</summary>

```bicep
module account 'br/public:avm/res/cognitive-services/account:<version>' = {
name: 'accountDeployment'
params: {
// Required parameters
kind: 'SpeechServices'
name: 'csakv001'
// Non-required parameters
location: '<location>'
secretsKeyVault: {
key1: 'custom-secret-name'
keyVaultName: '<keyVaultName>'
}
}
}
```

</details>
<p>

<details>

<summary>via JSON Parameter file</summary>

```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": "csakv001"
},
// Non-required parameters
"location": {
"value": "<location>"
},
"secretsKeyVault": {
"value": {
"key1": "custom-secret-name",
"keyVaultName": "<keyVaultName>"
}
}
}
}
```

</details>
<p>

### Example 3: _Using only defaults_

This instance deploys the module with the minimum set of required parameters.

Expand Down Expand Up @@ -183,7 +247,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 3: _Using large parameter set_
### Example 4: _Using large parameter set_

This instance deploys the module with most of its features enabled.

Expand Down Expand Up @@ -471,7 +535,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 4: _As Speech Service_
### Example 5: _As Speech Service_

This instance deploys the module as a Speech Service.

Expand Down Expand Up @@ -585,7 +649,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 5: _Using Customer-Managed-Keys with System-Assigned identity_
### Example 6: _Using Customer-Managed-Keys with System-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.

Expand Down Expand Up @@ -667,7 +731,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 6: _Using Customer-Managed-Keys with User-Assigned identity_
### Example 7: _Using Customer-Managed-Keys with User-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret.

Expand Down Expand Up @@ -755,7 +819,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>

### Example 7: _WAF-aligned_
### Example 8: _WAF-aligned_

This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.

Expand Down Expand Up @@ -929,6 +993,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
| [`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 assignments to create. |
| [`secretsKeyVault`](#parameter-secretskeyvault) | object | Key vault reference and secret settings to add the connection strings and keys generated by the storage account. |
| [`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. |
Expand Down Expand Up @@ -1870,6 +1935,55 @@ The principal type of the assigned principal ID.
]
```

### Parameter: `secretsKeyVault`

Key vault reference and secret settings to add the connection strings and keys generated by the storage account.

- Required: No
- Type: object

**Required parameters**

| Parameter | Type | Description |
| :-- | :-- | :-- |
| [`keyVaultName`](#parameter-secretskeyvaultkeyvaultname) | string | The key vault name where to store the keys and connection strings generated by the modules. |

**Optional parameters**

| Parameter | Type | Description |
| :-- | :-- | :-- |
| [`key1`](#parameter-secretskeyvaultkey1) | string | Default to CSKey1. The key1 secret name to create. |
| [`key2`](#parameter-secretskeyvaultkey2) | string | Default to CSKey2. The key2 secret name to create. |
| [`resourceGroupName`](#parameter-secretskeyvaultresourcegroupname) | string | Default to the resource group where this account is. The resource group name where the key vault is. |

### Parameter: `secretsKeyVault.keyVaultName`

The key vault name where to store the keys and connection strings generated by the modules.

- Required: Yes
- Type: string

### Parameter: `secretsKeyVault.key1`

Default to CSKey1. The key1 secret name to create.

- Required: No
- Type: string

### Parameter: `secretsKeyVault.key2`

Default to CSKey2. The key2 secret name to create.

- Required: No
- Type: string

### Parameter: `secretsKeyVault.resourceGroupName`

Default to the resource group where this account is. The resource group name where the key vault is.

- Required: No
- Type: string

### Parameter: `sku`

SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region.
Expand Down
119 changes: 75 additions & 44 deletions avm/res/cognitive-services/account/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ param enableTelemetry bool = true
@description('Optional. Array of deployments about cognitive service accounts to create.')
param deployments deploymentsType

@description('Optional. Key vault reference and secret settings to add the connection strings and keys generated by the storage account.')
param secretsKeyVault secretsKeyVaultType?

var formattedUserAssignedIdentities = reduce(
map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
{},
Expand Down Expand Up @@ -248,47 +251,43 @@ var builtInRoleNames = {
)
}

resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
if (enableTelemetry) {
name: '46d3xbcp.res.cognitiveservices-account.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
properties: {
mode: 'Incremental'
template: {
'$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
contentVersion: '1.0.0.0'
resources: []
outputs: {
telemetry: {
type: 'String'
value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
name: '46d3xbcp.res.cognitiveservices-account.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
properties: {
mode: 'Incremental'
template: {
'$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
contentVersion: '1.0.0.0'
resources: []
outputs: {
telemetry: {
type: 'String'
value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
}

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 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 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 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@2023-05-01' = {
name: name
Expand Down Expand Up @@ -352,17 +351,16 @@ resource cognitiveService_deployments 'Microsoft.CognitiveServices/accounts/depl
}
]

resource cognitiveService_lock 'Microsoft.Authorization/locks@2020-05-01' =
if (!empty(lock ?? {}) && lock.?kind != 'None') {
name: lock.?name ?? 'lock-${name}'
properties: {
level: lock.?kind ?? ''
notes: lock.?kind == 'CanNotDelete'
? 'Cannot delete resource or child resources.'
: 'Cannot delete or modify the resource or child resources.'
}
scope: cognitiveService
resource cognitiveService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
name: lock.?name ?? 'lock-${name}'
properties: {
level: lock.?kind ?? ''
notes: lock.?kind == 'CanNotDelete'
? 'Cannot delete resource or child resources.'
: 'Cannot delete or modify the resource or child resources.'
}
scope: cognitiveService
}

resource cognitiveService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
Expand Down Expand Up @@ -466,6 +464,25 @@ resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignmen
}
]

module keyVault 'modules/secrets-key-vault.bicep' = if (secretsKeyVault != null) {
name: '${uniqueString(deployment().name, location)}-secrets-kv'
scope: resourceGroup(secretsKeyVault.?resourceGroupName ?? resourceGroup().name)
params: {
keyVaultName: secretsKeyVault!.keyVaultName

keySecrets: [
{
secretName: secretsKeyVault.?key1 ?? 'CSKey1'
secretValue: cognitiveService.listKeys().key1
}
{
secretName: secretsKeyVault.?key2 ?? 'CSKey2'
secretValue: cognitiveService.listKeys().key2
}
]
}
}

@description('The name of the cognitive services account.')
output name string = cognitiveService.name

Expand Down Expand Up @@ -691,3 +708,17 @@ type deploymentsType = {
@description('Optional. The name of RAI policy.')
raiPolicyName: string?
}[]?

type secretsKeyVaultType = {
@description('Required. The key vault name where to store the keys and connection strings generated by the modules.')
keyVaultName: string

@description('Optional. Default to the resource group where this account is. The resource group name where the key vault is.')
resourceGroupName: string?

@description('Optional. Default to CSKey1. The key1 secret name to create.')
key1: string?

@description('Optional. Default to CSKey2. The key2 secret name to create.')
key2: string?
}
Loading