fix: Add key vault to cognitive service - avm/res/cognitive-services/account (#3222)

## Description

Follow-up to: #1932
cc: @Agazoth 

| Pipeline |
| -------- |

[![avm.res.cognitive-services.account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2FAgazoth%2FAddKVToCognitiveService&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml)

## Type of Change

<!-- Use the check-boxes [x] on the options that are relevant. -->

- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [x] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
  - [ ] Update to documentation

## Checklist

- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [x] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [x] My corresponding pipelines / checks run clean and green without
any errors or warnings

<!-- Please keep up to day with the contribution guide at
https://aka.ms/avm/contribute/bicep -->

---------

Co-authored-by: Axel B. Andersen <axel.boeg.andersen@atea.dk>
Co-authored-by: Javier Cevallos <javier.cevallos@microsoft.com>
This commit is contained in:
Alexander Sehr 2024-09-18 05:45:40 +02:00 коммит произвёл GitHub
Родитель f21032a311
Коммит 7876f362de
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 530 добавлений и 15 удалений

Просмотреть файл

@ -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` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) |
| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) |
| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) |
@ -33,13 +34,14 @@ The following section provides usage examples for the module, which were used to
- [Using `AIServices` with `deployments` in parameter set and private endpoints](#example-1-using-aiservices-with-deployments-in-parameter-set-and-private-endpoints)
- [Using `AIServices` with `deployments` in parameter set](#example-2-using-aiservices-with-deployments-in-parameter-set)
- [Using only defaults](#example-3-using-only-defaults)
- [Using large parameter set](#example-4-using-large-parameter-set)
- [Using `OpenAI` and `deployments` in parameter set with private endpoint](#example-5-using-openai-and-deployments-in-parameter-set-with-private-endpoint)
- [As Speech Service](#example-6-as-speech-service)
- [Using Customer-Managed-Keys with System-Assigned identity](#example-7-using-customer-managed-keys-with-system-assigned-identity)
- [Using Customer-Managed-Keys with User-Assigned identity](#example-8-using-customer-managed-keys-with-user-assigned-identity)
- [WAF-aligned](#example-9-waf-aligned)
- [Storing keys of service in key vault](#example-3-storing-keys-of-service-in-key-vault)
- [Using only defaults](#example-4-using-only-defaults)
- [Using large parameter set](#example-5-using-large-parameter-set)
- [Using `OpenAI` and `deployments` in parameter set with private endpoint](#example-6-using-openai-and-deployments-in-parameter-set-with-private-endpoint)
- [As Speech Service](#example-7-as-speech-service)
- [Using Customer-Managed-Keys with System-Assigned identity](#example-8-using-customer-managed-keys-with-system-assigned-identity)
- [Using Customer-Managed-Keys with User-Assigned identity](#example-9-using-customer-managed-keys-with-user-assigned-identity)
- [WAF-aligned](#example-10-waf-aligned)
### Example 1: _Using `AIServices` with `deployments` in parameter set and private endpoints_
@ -237,7 +239,71 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 3: _Using only defaults_
### Example 3: _Storing keys of service in key vault_
This instance deploys the module and stores its keys in a 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>'
secretsExportConfiguration: {
accessKey1Name: 'csakv001-accessKey1'
accessKey2Name: 'csakv001-accessKey2'
keyVaultResourceId: '<keyVaultResourceId>'
}
}
}
```
</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>"
},
"secretsExportConfiguration": {
"value": {
"accessKey1Name": "csakv001-accessKey1",
"accessKey2Name": "csakv001-accessKey2",
"keyVaultResourceId": "<keyVaultResourceId>"
}
}
}
}
```
</details>
<p>
### Example 4: _Using only defaults_
This instance deploys the module with the minimum set of required parameters.
@ -289,7 +355,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 4: _Using large parameter set_
### Example 5: _Using large parameter set_
This instance deploys the module with most of its features enabled.
@ -581,7 +647,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 5: _Using `OpenAI` and `deployments` in parameter set with private endpoint_
### Example 6: _Using `OpenAI` and `deployments` in parameter set with private endpoint_
This instance deploys the module with the AI model deployment feature and private endpoint.
@ -689,7 +755,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 6: _As Speech Service_
### Example 7: _As Speech Service_
This instance deploys the module as a Speech Service.
@ -803,7 +869,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 7: _Using Customer-Managed-Keys with System-Assigned identity_
### Example 8: _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.
@ -885,7 +951,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 8: _Using Customer-Managed-Keys with User-Assigned identity_
### Example 9: _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.
@ -973,7 +1039,7 @@ module account 'br/public:avm/res/cognitive-services/account:<version>' = {
</details>
<p>
### Example 9: _WAF-aligned_
### Example 10: _WAF-aligned_
This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
@ -1146,6 +1212,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. |
| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. |
| [`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. |
@ -2142,6 +2209,47 @@ The principal type of the assigned principal ID.
]
```
### Parameter: `secretsExportConfiguration`
Key vault reference and secret settings for the module's secrets export.
- Required: No
- Type: object
**Required parameters**
| Parameter | Type | Description |
| :-- | :-- | :-- |
| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The key vault name where to store the keys and connection strings generated by the modules. |
**Optional parameters**
| Parameter | Type | Description |
| :-- | :-- | :-- |
| [`accessKey1Name`](#parameter-secretsexportconfigurationaccesskey1name) | string | The name for the accessKey1 secret to create. |
| [`accessKey2Name`](#parameter-secretsexportconfigurationaccesskey2name) | string | The name for the accessKey2 secret to create. |
### Parameter: `secretsExportConfiguration.keyVaultResourceId`
The key vault name where to store the keys and connection strings generated by the modules.
- Required: Yes
- Type: string
### Parameter: `secretsExportConfiguration.accessKey1Name`
The name for the accessKey1 secret to create.
- Required: No
- Type: string
### Parameter: `secretsExportConfiguration.accessKey2Name`
The name for the accessKey2 secret to create.
- 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.
@ -2192,6 +2300,7 @@ The storage accounts for this resource.
| :-- | :-- | :-- |
| `endpoint` | string | The service endpoint of the cognitive services account. |
| `endpoints` | | All endpoints available for the cognitive services account, types depends on the cognitive service kind. |
| `exportedSecrets` | | A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name. |
| `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. |

Просмотреть файл

@ -123,6 +123,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 for the module\'s secrets export.')
param secretsExportConfiguration secretsExportConfigurationType?
var formattedUserAssignedIdentities = reduce(
map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
{},
@ -468,6 +471,36 @@ resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignmen
}
]
module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
name: '${uniqueString(deployment().name, location)}-secrets-kv'
scope: resourceGroup(
split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2],
split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4]
)
params: {
keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/'))
secretsToSet: union(
[],
contains(secretsExportConfiguration!, 'accessKey1Name')
? [
{
name: secretsExportConfiguration!.accessKey1Name
value: cognitiveService.listKeys().key1
}
]
: [],
contains(secretsExportConfiguration!, 'accessKey2Name')
? [
{
name: secretsExportConfiguration!.accessKey2Name
value: cognitiveService.listKeys().key2
}
]
: []
)
}
}
@description('The name of the cognitive services account.')
output name string = cognitiveService.name
@ -489,6 +522,11 @@ output systemAssignedMIPrincipalId string = cognitiveService.?identity.?principa
@description('The location the resource was deployed into.')
output location string = cognitiveService.location
@description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
: {}
// ================ //
// Definitions //
// ================ //
@ -706,3 +744,20 @@ type endpointsType = {
@description('The endpoint URI.')
endpoint: string?
}?
type secretsExportConfigurationType = {
@description('Required. The key vault name where to store the keys and connection strings generated by the modules.')
keyVaultResourceId: string
@description('Optional. The name for the accessKey1 secret to create.')
accessKey1Name: string?
@description('Optional. The name for the accessKey2 secret to create.')
accessKey2Name: string?
}
import { secretSetType } from 'modules/keyVaultExport.bicep'
type secretsOutputType = {
@description('An exported secret\'s references.')
*: secretSetType
}

Просмотреть файл

@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "0.29.47.4906",
"templateHash": "14735378599748380229"
"templateHash": "259342811715055071"
},
"name": "Cognitive Services",
"description": "This module deploys a Cognitive Service.",
@ -573,6 +573,63 @@
}
},
"nullable": true
},
"secretsExportConfigurationType": {
"type": "object",
"properties": {
"keyVaultResourceId": {
"type": "string",
"metadata": {
"description": "Required. The key vault name where to store the keys and connection strings generated by the modules."
}
},
"accessKey1Name": {
"type": "string",
"nullable": true,
"metadata": {
"description": "Optional. The name for the accessKey1 secret to create."
}
},
"accessKey2Name": {
"type": "string",
"nullable": true,
"metadata": {
"description": "Optional. The name for the accessKey2 secret to create."
}
}
}
},
"secretsOutputType": {
"type": "object",
"properties": {},
"additionalProperties": {
"$ref": "#/definitions/secretSetType",
"metadata": {
"description": "An exported secret's references."
}
}
},
"secretSetType": {
"type": "object",
"properties": {
"secretResourceId": {
"type": "string",
"metadata": {
"description": "The resourceId of the exported secret."
}
},
"secretUri": {
"type": "string",
"metadata": {
"description": "The secret URI of the exported secret."
}
}
},
"metadata": {
"__bicep_imported_from!": {
"sourceTemplate": "modules/keyVaultExport.bicep"
}
}
}
},
"parameters": {
@ -783,6 +840,13 @@
"metadata": {
"description": "Optional. Array of deployments about cognitive service accounts to create."
}
},
"secretsExportConfiguration": {
"$ref": "#/definitions/secretsExportConfigurationType",
"nullable": true,
"metadata": {
"description": "Optional. Key vault reference and secret settings for the module's secrets export."
}
}
},
"variables": {
@ -1692,6 +1756,140 @@
"dependsOn": [
"cognitiveService"
]
},
"secretsExport": {
"condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]",
"subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]",
"resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
},
"mode": "Incremental",
"parameters": {
"keyVaultName": {
"value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]"
},
"secretsToSet": {
"value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1Name, 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2Name, 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]"
}
},
"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.29.47.4906",
"templateHash": "986606208324987345"
}
},
"definitions": {
"secretSetType": {
"type": "object",
"properties": {
"secretResourceId": {
"type": "string",
"metadata": {
"description": "The resourceId of the exported secret."
}
},
"secretUri": {
"type": "string",
"metadata": {
"description": "The secret URI of the exported secret."
}
}
},
"metadata": {
"__bicep_export!": true
}
},
"secretToSetType": {
"type": "object",
"properties": {
"name": {
"type": "string",
"metadata": {
"description": "Required. The name of the secret to set."
}
},
"value": {
"type": "securestring",
"metadata": {
"description": "Required. The value of the secret to set."
}
}
}
}
},
"parameters": {
"keyVaultName": {
"type": "string",
"metadata": {
"description": "Required. The name of the Key Vault to set the ecrets in."
}
},
"secretsToSet": {
"type": "array",
"items": {
"$ref": "#/definitions/secretToSetType"
},
"metadata": {
"description": "Required. The secrets to set in the Key Vault."
}
}
},
"resources": {
"keyVault": {
"existing": true,
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2022-07-01",
"name": "[parameters('keyVaultName')]"
},
"secrets": {
"copy": {
"name": "secrets",
"count": "[length(parameters('secretsToSet'))]"
},
"type": "Microsoft.KeyVault/vaults/secrets",
"apiVersion": "2023-07-01",
"name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]",
"properties": {
"value": "[parameters('secretsToSet')[copyIndex()].value]"
},
"dependsOn": [
"keyVault"
]
}
},
"outputs": {
"secretsSet": {
"type": "array",
"items": {
"$ref": "#/definitions/secretSetType"
},
"metadata": {
"description": "The references to the secrets exported to the provided Key Vault."
},
"copy": {
"count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]",
"input": {
"secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]",
"secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]"
}
}
}
}
}
},
"dependsOn": [
"cognitiveService"
]
}
},
"outputs": {
@ -1743,6 +1941,13 @@
"description": "The location the resource was deployed into."
},
"value": "[reference('cognitiveService', '2023-05-01', 'full').location]"
},
"exportedSecrets": {
"$ref": "#/definitions/secretsOutputType",
"metadata": {
"description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name."
},
"value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]"
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
// ============== //
// Parameters //
// ============== //
@description('Required. The name of the Key Vault to set the ecrets in.')
param keyVaultName string
@description('Required. The secrets to set in the Key Vault.')
param secretsToSet secretToSetType[]
// ============= //
// Resources //
// ============= //
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
name: keyVaultName
}
resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [
for secret in secretsToSet: {
name: secret.name
parent: keyVault
properties: {
value: secret.value
}
}
]
// =========== //
// Outputs //
// =========== //
@description('The references to the secrets exported to the provided Key Vault.')
output secretsSet secretSetType[] = [
#disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value
for index in range(0, length(secretsToSet ?? [])): {
secretResourceId: secrets[index].id
secretUri: secrets[index].properties.secretUri
}
]
// =============== //
// Definitions //
// =============== //
@export()
type secretSetType = {
@description('The resourceId of the exported secret.')
secretResourceId: string
@description('The secret URI of the exported secret.')
secretUri: string
}
type secretToSetType = {
@description('Required. The name of the secret to set.')
name: string
@description('Required. The value of the secret to set.')
@secure()
value: string
}

Просмотреть файл

@ -0,0 +1,21 @@
@description('Optional. The location to deploy to.')
param location string = resourceGroup().location
@description('Required. The name of the Managed Identity to create.')
param keyVaultName string
resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = {
name: keyVaultName
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
enableRbacAuthorization: true
tenantId: subscription().tenantId
}
}
@description('The name of the Key Vault created.')
output keyVaultResourceId string = keyVault.id

Просмотреть файл

@ -0,0 +1,63 @@
targetScope = 'subscription'
metadata name = 'Storing keys of service in key vault'
metadata description = 'This instance deploys the module and stores its keys in a key vault.'
// ========== //
// Parameters //
// ========== //
@description('Optional. The name of the resource group to deploy for testing purposes.')
@maxLength(90)
param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg'
@description('Optional. The location to deploy resources to.')
param resourceLocation 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 = 'csakv'
@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
param namePrefix string = '#_namePrefix_#'
// ============ //
// Dependencies //
// ============ //
module nestedDependencies 'dependencies.bicep' = {
scope: resourceGroup
name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
params: {
keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}'
location: resourceLocation
}
}
// General resources
// =================
resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
name: resourceGroupName
location: resourceLocation
}
// ============== //
// Test Execution //
// ============== //
@batchSize(1)
module testDeployment '../../../main.bicep' = [
for iteration in ['init', 'idem']: {
scope: resourceGroup
name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
params: {
name: '${namePrefix}${serviceShort}001'
kind: 'SpeechServices'
location: resourceLocation
secretsExportConfiguration: {
keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
accessKey1Name: '${namePrefix}${serviceShort}001-accessKey1'
accessKey2Name: '${namePrefix}${serviceShort}001-accessKey2'
}
}
}
]