From 8e759bbdbdba5841dde425761a9cbe323d4c9d07 Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Fri, 7 Feb 2025 08:36:19 +0100 Subject: [PATCH 01/53] Initial commit for hybrid-compute/license module --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.res.hybrid-compute.license.yml | 88 +++++ avm/res/hybrid-compute/license/README.md | 350 ++++++++++++++++++ avm/res/hybrid-compute/license/main.bicep | 114 ++++++ avm/res/hybrid-compute/license/main.json | 216 +++++++++++ .../tests/e2e/defaults/main.test.bicep | 50 +++ .../tests/e2e/waf-aligned/main.test.bicep | 51 +++ avm/res/hybrid-compute/license/version.json | 7 + 9 files changed, 878 insertions(+) create mode 100644 .github/workflows/avm.res.hybrid-compute.license.yml create mode 100644 avm/res/hybrid-compute/license/README.md create mode 100644 avm/res/hybrid-compute/license/main.bicep create mode 100644 avm/res/hybrid-compute/license/main.json create mode 100644 avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/hybrid-compute/license/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1c1f38b409..2a1ac4c544 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -88,6 +88,7 @@ /avm/res/fabric/capacity/ @Azure/avm-res-fabric-capacity-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/health-bot/health-bot/ @Azure/avm-res-healthbot-healthbot-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/healthcare-apis/workspace/ @Azure/avm-res-healthcareapis-workspace-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/hybrid-compute/license/ @Azure/avm-res-hybridcompute-license-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/hybrid-compute/machine/ @Azure/avm-res-hybridcompute-machine-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/action-group/ @Azure/avm-res-insights-actiongroup-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/activity-log-alert/ @Azure/avm-res-insights-activitylogalert-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 7e02a41292..8518406122 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -123,6 +123,7 @@ body: - "avm/res/fabric/capacity" - "avm/res/health-bot/health-bot" - "avm/res/healthcare-apis/workspace" + - "avm/res/hybrid-compute/license" - "avm/res/hybrid-compute/machine" - "avm/res/insights/action-group" - "avm/res/insights/activity-log-alert" diff --git a/.github/workflows/avm.res.hybrid-compute.license.yml b/.github/workflows/avm.res.hybrid-compute.license.yml new file mode 100644 index 0000000000..0e8644bd24 --- /dev/null +++ b/.github/workflows/avm.res.hybrid-compute.license.yml @@ -0,0 +1,88 @@ +name: "avm.res.hybrid-compute.license" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.hybrid-compute.license.yml" + - "avm/res/hybrid-compute/license/**" + - "utilities/pipelines/**" + - "!utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/hybrid-compute/license" + workflowPath: ".github/workflows/avm.res.hybrid-compute.license.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md new file mode 100644 index 0000000000..9fe6254266 --- /dev/null +++ b/avm/res/hybrid-compute/license/README.md @@ -0,0 +1,350 @@ +# Azure Arc License `[Microsoft.HybridCompute/licenses]` + +This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.HybridCompute/licenses` | [2024-11-10-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-11-10-preview/licenses) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/hybrid-compute/license:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [WAF-aligned](#example-2-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +
+ +via Bicep module + +```bicep +module license 'br/public:avm/res/hybrid-compute/license:' = { + name: 'licenseDeployment' + params: { + // Required parameters + name: 'hclmin001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/hybrid-compute/license:' + +// Required parameters +param name = 'hclmin001' +// Non-required parameters +param location = '' +``` + +
+

+ +### Example 2: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module license 'br/public:avm/res/hybrid-compute/license:' = { + name: 'licenseDeployment' + params: { + // Required parameters + name: 'hclwaf001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/hybrid-compute/license:' + +// Required parameters +param name = 'hclwaf001' +// Non-required parameters +param location = '' +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Azure Arc License to be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`licenseDetailEdition`](#parameter-licensedetailedition) | string | Describes the edition of the license. The values are either Standard or Datacenter.. | +| [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Describes the number of processors. | +| [`licenseDetailState`](#parameter-licensedetailstate) | string | Describes the license state. | +| [`licenseDetailTarget`](#parameter-licensedetailtarget) | string | Describes the license target server. | +| [`licenseDetailType`](#parameter-licensedetailtype) | string | Describes the license core type (pCore or vCore). | +| [`licenseType`](#parameter-licensetype) | string | The type of the license resource. The value is ESU. | +| [`licenseVolumeLicenseDetails`](#parameter-licensevolumelicensedetails) | array | A list of volume license details. | +| [`location`](#parameter-location) | string | The location of the Azure Arc License to be created. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`tenantId`](#parameter-tenantid) | string | The tenant ID of the license resource. Default is the tenant ID of the current subscription. | + +### Parameter: `name` + +The name of the Azure Arc License to be created. + +- Required: Yes +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `licenseDetailEdition` + +Describes the edition of the license. The values are either Standard or Datacenter.. + +- Required: No +- Type: string +- Default: `'Standard'` +- Allowed: + ```Bicep + [ + 'Datacenter' + 'Standard' + ] + ``` + +### Parameter: `licenseDetailProcessors` + +Describes the number of processors. + +- Required: No +- Type: int +- Default: `2` + +### Parameter: `licenseDetailState` + +Describes the license state. + +- Required: No +- Type: string +- Default: `'Active'` +- Allowed: + ```Bicep + [ + 'Active' + 'Deactivated' + ] + ``` + +### Parameter: `licenseDetailTarget` + +Describes the license target server. + +- Required: No +- Type: string +- Default: `'Windows Server 2012 R2'` +- Allowed: + ```Bicep + [ + 'Windows Server 2012' + 'Windows Server 2012 R2' + ] + ``` + +### Parameter: `licenseDetailType` + +Describes the license core type (pCore or vCore). + +- Required: No +- Type: string +- Default: `'vCore'` +- Allowed: + ```Bicep + [ + 'pCore' + 'vCore' + ] + ``` + +### Parameter: `licenseType` + +The type of the license resource. The value is ESU. + +- Required: No +- Type: string +- Default: `'ESU'` +- Allowed: + ```Bicep + [ + 'ESU' + ] + ``` + +### Parameter: `licenseVolumeLicenseDetails` + +A list of volume license details. + +- Required: No +- Type: array +- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`invoiceId`](#parameter-licensevolumelicensedetailsinvoiceid) | string | The invoice id for the volume license. | +| [`programYear`](#parameter-licensevolumelicensedetailsprogramyear) | string | Describes the program year the volume license is for. | + +### Parameter: `licenseVolumeLicenseDetails.invoiceId` + +The invoice id for the volume license. + +- Required: Yes +- Type: string + +### Parameter: `licenseVolumeLicenseDetails.programYear` + +Describes the program year the volume license is for. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Year 1' + 'Year 2' + 'Year 3' + ] + ``` + +### Parameter: `location` + +The location of the Azure Arc License to be created. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `tenantId` + +The tenant ID of the license resource. Default is the tenant ID of the current subscription. + +- Required: No +- Type: string +- Default: `[tenant().tenantId]` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the machine. | +| `resourceGroupName` | string | The name of the resource group the VM was created in. | +| `resourceId` | string | The resource ID of the machine. | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep new file mode 100644 index 0000000000..6bbea1b979 --- /dev/null +++ b/avm/res/hybrid-compute/license/main.bicep @@ -0,0 +1,114 @@ +metadata name = 'Azure Arc License' +metadata description = 'This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process.' + +@description('Required. The name of the Azure Arc License to be created.') +param name string + +@description('Optional. The location of the Azure Arc License to be created.') +param location string = resourceGroup().location + +@description('Optional. Describes the edition of the license. The values are either Standard or Datacenter..') +@allowed([ + 'Standard' + 'Datacenter' +]) +param licenseDetailEdition string = 'Standard' + +@description('Optional. Describes the number of processors.') +param licenseDetailProcessors int = 2 + +@description('Optional. Describes the license state.') +@allowed([ + 'Active' + 'Deactivated' +]) +param licenseDetailState string = 'Active' + +@description('Optional. Describes the license target server.') +@allowed([ + 'Windows Server 2012 R2' + 'Windows Server 2012' +]) +param licenseDetailTarget string = 'Windows Server 2012 R2' + +@description('Optional. Describes the license core type (pCore or vCore).') +@allowed([ + 'pCore' + 'vCore' +]) +param licenseDetailType string = 'vCore' + +@description('Optional. A list of volume license details.') +param licenseVolumeLicenseDetails volumeLicenseDetailType[] = [] + +@description('Optional. The type of the license resource. The value is ESU.') +@allowed([ + 'ESU' +]) +param licenseType string = 'ESU' + +@description('Optional. The tenant ID of the license resource. Default is the tenant ID of the current subscription.') +param tenantId string = tenant().tenantId + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource license 'Microsoft.HybridCompute/licenses@2024-11-10-preview' = { + name: name + location: location + properties: { + licenseDetails: { + edition: licenseDetailEdition + processors: licenseDetailProcessors + state: licenseDetailState + target: licenseDetailTarget + type: licenseDetailType + volumeLicenseDetails: licenseVolumeLicenseDetails + } + licenseType: licenseType + tenantId: tenantId + } + tags: tags +} + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.res.hybridcompute-license.${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: '0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +type volumeLicenseDetailType = { + @description('Required. The invoice id for the volume license.') + invoiceId: string + + @description('Required. Describes the program year the volume license is for.') + programYear: 'Year 1' | 'Year 2' | 'Year 3' +} + +@description('The name of the machine.') +output name string = license.name + +@description('The resource ID of the machine.') +output resourceId string = license.id + +@description('The name of the resource group the VM was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = license.location diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json new file mode 100644 index 0000000000..54f2d26f51 --- /dev/null +++ b/avm/res/hybrid-compute/license/main.json @@ -0,0 +1,216 @@ +{ + "$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.33.93.31351", + "templateHash": "17846338208131133417" + }, + "name": "Azure Arc License", + "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." + }, + "definitions": { + "volumeLicenseDetailType": { + "type": "object", + "properties": { + "invoiceId": { + "type": "string", + "metadata": { + "description": "Required. The invoice id for the volume license." + } + }, + "programYear": { + "type": "string", + "allowedValues": [ + "Year 1", + "Year 2", + "Year 3" + ], + "metadata": { + "description": "Required. Describes the program year the volume license is for." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Arc License to be created." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the Azure Arc License to be created." + } + }, + "licenseDetailEdition": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "Datacenter" + ], + "metadata": { + "description": "Optional. Describes the edition of the license. The values are either Standard or Datacenter.." + } + }, + "licenseDetailProcessors": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. Describes the number of processors." + } + }, + "licenseDetailState": { + "type": "string", + "defaultValue": "Active", + "allowedValues": [ + "Active", + "Deactivated" + ], + "metadata": { + "description": "Optional. Describes the license state." + } + }, + "licenseDetailTarget": { + "type": "string", + "defaultValue": "Windows Server 2012 R2", + "allowedValues": [ + "Windows Server 2012 R2", + "Windows Server 2012" + ], + "metadata": { + "description": "Optional. Describes the license target server." + } + }, + "licenseDetailType": { + "type": "string", + "defaultValue": "vCore", + "allowedValues": [ + "pCore", + "vCore" + ], + "metadata": { + "description": "Optional. Describes the license core type (pCore or vCore)." + } + }, + "licenseVolumeLicenseDetails": { + "type": "array", + "items": { + "$ref": "#/definitions/volumeLicenseDetailType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. A list of volume license details." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "ESU", + "allowedValues": [ + "ESU" + ], + "metadata": { + "description": "Optional. The type of the license resource. The value is ESU." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "Optional. The tenant ID of the license resource. Default is the tenant ID of the current subscription." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "license": { + "type": "Microsoft.HybridCompute/licenses", + "apiVersion": "2024-11-10-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "licenseDetails": { + "edition": "[parameters('licenseDetailEdition')]", + "processors": "[parameters('licenseDetailProcessors')]", + "state": "[parameters('licenseDetailState')]", + "target": "[parameters('licenseDetailTarget')]", + "type": "[parameters('licenseDetailType')]", + "volumeLicenseDetails": "[parameters('licenseVolumeLicenseDetails')]" + }, + "licenseType": "[parameters('licenseType')]", + "tenantId": "[parameters('tenantId')]" + }, + "tags": "[parameters('tags')]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.hybridcompute-license.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the machine." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the machine." + }, + "value": "[resourceId('Microsoft.HybridCompute/licenses', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the VM was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('license', '2024-11-10-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..9de5537bf2 --- /dev/null +++ b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,50 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}---${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.') +// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test +param serviceShort string = 'hclmin' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-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' + location: resourceLocation + } + } +] diff --git a/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..5fffd994bd --- /dev/null +++ b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}---${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.') +// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test +param serviceShort string = 'hclwaf' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-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: { + // You parameters go here + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + } + } +] diff --git a/avm/res/hybrid-compute/license/version.json b/avm/res/hybrid-compute/license/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/hybrid-compute/license/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 179d70ffafb76fefdc82752ddc1c4e018d1e19fa Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Sat, 8 Feb 2025 08:20:29 +0100 Subject: [PATCH 02/53] Updated descriptions and min value for processors --- avm/res/hybrid-compute/license/README.md | 27 ++++++++++++++++------- avm/res/hybrid-compute/license/main.bicep | 11 ++++----- avm/res/hybrid-compute/license/main.json | 13 ++++++----- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md index 9fe6254266..c074623605 100644 --- a/avm/res/hybrid-compute/license/README.md +++ b/avm/res/hybrid-compute/license/README.md @@ -169,10 +169,10 @@ param location = '' | :-- | :-- | :-- | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`licenseDetailEdition`](#parameter-licensedetailedition) | string | Describes the edition of the license. The values are either Standard or Datacenter.. | -| [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Describes the number of processors. | -| [`licenseDetailState`](#parameter-licensedetailstate) | string | Describes the license state. | +| [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license. | +| [`licenseDetailState`](#parameter-licensedetailstate) | string | Activate or Deactivate the license billing cycle. Billing will not start until the license is activated. | | [`licenseDetailTarget`](#parameter-licensedetailtarget) | string | Describes the license target server. | -| [`licenseDetailType`](#parameter-licensedetailtype) | string | Describes the license core type (pCore or vCore). | +| [`licenseDetailType`](#parameter-licensedetailtype) | string | Provide the core type (vCore or pCore) needed for this ESU licens. | | [`licenseType`](#parameter-licensetype) | string | The type of the license resource. The value is ESU. | | [`licenseVolumeLicenseDetails`](#parameter-licensevolumelicensedetails) | array | A list of volume license details. | | [`location`](#parameter-location) | string | The location of the Azure Arc License to be created. | @@ -211,19 +211,20 @@ Describes the edition of the license. The values are either Standard or Datacent ### Parameter: `licenseDetailProcessors` -Describes the number of processors. +Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license. - Required: No - Type: int -- Default: `2` +- Default: `8` +- MinValue: 8 ### Parameter: `licenseDetailState` -Describes the license state. +Activate or Deactivate the license billing cycle. Billing will not start until the license is activated. - Required: No - Type: string -- Default: `'Active'` +- Default: `'Deactivated'` - Allowed: ```Bicep [ @@ -231,6 +232,7 @@ Describes the license state. 'Deactivated' ] ``` +- MinValue: 8 ### Parameter: `licenseDetailTarget` @@ -246,10 +248,11 @@ Describes the license target server. 'Windows Server 2012 R2' ] ``` +- MinValue: 8 ### Parameter: `licenseDetailType` -Describes the license core type (pCore or vCore). +Provide the core type (vCore or pCore) needed for this ESU licens. - Required: No - Type: string @@ -261,6 +264,7 @@ Describes the license core type (pCore or vCore). 'vCore' ] ``` +- MinValue: 8 ### Parameter: `licenseType` @@ -275,6 +279,7 @@ The type of the license resource. The value is ESU. 'ESU' ] ``` +- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails` @@ -283,6 +288,7 @@ A list of volume license details. - Required: No - Type: array - Default: `[]` +- MinValue: 8 **Required parameters** @@ -297,6 +303,7 @@ The invoice id for the volume license. - Required: Yes - Type: string +- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails.programYear` @@ -312,6 +319,7 @@ Describes the program year the volume license is for. 'Year 3' ] ``` +- MinValue: 8 ### Parameter: `location` @@ -320,6 +328,7 @@ The location of the Azure Arc License to be created. - Required: No - Type: string - Default: `[resourceGroup().location]` +- MinValue: 8 ### Parameter: `tags` @@ -327,6 +336,7 @@ Tags of the resource. - Required: No - Type: object +- MinValue: 8 ### Parameter: `tenantId` @@ -335,6 +345,7 @@ The tenant ID of the license resource. Default is the tenant ID of the current s - Required: No - Type: string - Default: `[tenant().tenantId]` +- MinValue: 8 ## Outputs diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep index 6bbea1b979..d418e582bb 100644 --- a/avm/res/hybrid-compute/license/main.bicep +++ b/avm/res/hybrid-compute/license/main.bicep @@ -14,15 +14,16 @@ param location string = resourceGroup().location ]) param licenseDetailEdition string = 'Standard' -@description('Optional. Describes the number of processors.') -param licenseDetailProcessors int = 2 +@description('Optional. Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license.') +@minValue(8) +param licenseDetailProcessors int = 8 -@description('Optional. Describes the license state.') +@description('Optional. Activate or Deactivate the license billing cycle. Billing will not start until the license is activated.') @allowed([ 'Active' 'Deactivated' ]) -param licenseDetailState string = 'Active' +param licenseDetailState string = 'Deactivated' @description('Optional. Describes the license target server.') @allowed([ @@ -31,7 +32,7 @@ param licenseDetailState string = 'Active' ]) param licenseDetailTarget string = 'Windows Server 2012 R2' -@description('Optional. Describes the license core type (pCore or vCore).') +@description('Optional. Provide the core type (vCore or pCore) needed for this ESU licens.') @allowed([ 'pCore' 'vCore' diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index 54f2d26f51..e4bf8c4959 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "17846338208131133417" + "templateHash": "2450279209791185954" }, "name": "Azure Arc License", "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." @@ -62,20 +62,21 @@ }, "licenseDetailProcessors": { "type": "int", - "defaultValue": 2, + "defaultValue": 8, + "minValue": 8, "metadata": { - "description": "Optional. Describes the number of processors." + "description": "Optional. Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license." } }, "licenseDetailState": { "type": "string", - "defaultValue": "Active", + "defaultValue": "Deactivated", "allowedValues": [ "Active", "Deactivated" ], "metadata": { - "description": "Optional. Describes the license state." + "description": "Optional. Activate or Deactivate the license billing cycle. Billing will not start until the license is activated." } }, "licenseDetailTarget": { @@ -97,7 +98,7 @@ "vCore" ], "metadata": { - "description": "Optional. Describes the license core type (pCore or vCore)." + "description": "Optional. Provide the core type (vCore or pCore) needed for this ESU licens." } }, "licenseVolumeLicenseDetails": { From 3f5dfe21df3c7435ef05eda09514cc962c450063 Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Sat, 8 Feb 2025 08:33:27 +0100 Subject: [PATCH 03/53] fix invalid resource group name in tests --- .../hybrid-compute/license/tests/e2e/defaults/main.test.bicep | 2 +- .../license/tests/e2e/waf-aligned/main.test.bicep | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep index 9de5537bf2..e601f8aaec 100644 --- a/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep +++ b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep @@ -10,7 +10,7 @@ metadata description = 'This instance deploys the module with the minimum set of @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) // e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' -param resourceGroupName string = 'dep-${namePrefix}---${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.license-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location diff --git a/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep index 5fffd994bd..f205cc7f9d 100644 --- a/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep @@ -10,7 +10,7 @@ metadata description = 'This instance deploys the module in alignment with the b @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) // e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' -param resourceGroupName string = 'dep-${namePrefix}---${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.license-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location From 8dc36112e5d9335895977e821349a875befff457 Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Sat, 8 Feb 2025 08:43:06 +0100 Subject: [PATCH 04/53] Changed to api version that dont fail --- avm/res/hybrid-compute/license/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep index d418e582bb..3879ef9287 100644 --- a/avm/res/hybrid-compute/license/main.bicep +++ b/avm/res/hybrid-compute/license/main.bicep @@ -57,7 +57,7 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true -resource license 'Microsoft.HybridCompute/licenses@2024-11-10-preview' = { +resource license 'Microsoft.HybridCompute/licenses@2024-07-31-preview' = { name: name location: location properties: { From 4191b6b927517b6f12a6e0b0a83144b1ec52e10e Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Sat, 8 Feb 2025 08:46:29 +0100 Subject: [PATCH 05/53] updated readme and main.json --- avm/res/hybrid-compute/license/README.md | 2 +- avm/res/hybrid-compute/license/main.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md index c074623605..d1db548b8e 100644 --- a/avm/res/hybrid-compute/license/README.md +++ b/avm/res/hybrid-compute/license/README.md @@ -14,7 +14,7 @@ This module deploys an Azure Arc License for use with Azure Arc-enabled servers. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.HybridCompute/licenses` | [2024-11-10-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-11-10-preview/licenses) | +| `Microsoft.HybridCompute/licenses` | [2024-07-31-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-07-31-preview/licenses) | ## Usage examples diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index e4bf8c4959..2aa99521a0 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "2450279209791185954" + "templateHash": "4063370187688285408" }, "name": "Azure Arc License", "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." @@ -146,7 +146,7 @@ "resources": { "license": { "type": "Microsoft.HybridCompute/licenses", - "apiVersion": "2024-11-10-preview", + "apiVersion": "2024-07-31-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", "properties": { @@ -211,7 +211,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('license', '2024-11-10-preview', 'full').location]" + "value": "[reference('license', '2024-07-31-preview', 'full').location]" } } } \ No newline at end of file From 9fff3395a6dbc9ce298e8816599097d4709764f8 Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Sat, 8 Feb 2025 19:55:56 +0100 Subject: [PATCH 06/53] Updated according to PR comments --- avm/res/hybrid-compute/license/README.md | 22 ------------------- avm/res/hybrid-compute/license/main.bicep | 2 ++ avm/res/hybrid-compute/license/main.json | 6 ++++- .../tests/e2e/defaults/main.test.bicep | 3 --- .../tests/e2e/waf-aligned/main.test.bicep | 4 ---- 5 files changed, 7 insertions(+), 30 deletions(-) diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md index d1db548b8e..676a4ba112 100644 --- a/avm/res/hybrid-compute/license/README.md +++ b/avm/res/hybrid-compute/license/README.md @@ -40,10 +40,7 @@ This instance deploys the module with the minimum set of required parameters. module license 'br/public:avm/res/hybrid-compute/license:' = { name: 'licenseDeployment' params: { - // Required parameters name: 'hclmin001' - // Non-required parameters - location: '' } } ``` @@ -60,13 +57,8 @@ module license 'br/public:avm/res/hybrid-compute/license:' = { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // Required parameters "name": { "value": "hclmin001" - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -82,10 +74,7 @@ module license 'br/public:avm/res/hybrid-compute/license:' = { ```bicep-params using 'br/public:avm/res/hybrid-compute/license:' -// Required parameters param name = 'hclmin001' -// Non-required parameters -param location = '' ``` @@ -104,10 +93,7 @@ This instance deploys the module in alignment with the best-practices of the Azu module license 'br/public:avm/res/hybrid-compute/license:' = { name: 'licenseDeployment' params: { - // Required parameters name: 'hclwaf001' - // Non-required parameters - location: '' } } ``` @@ -124,13 +110,8 @@ module license 'br/public:avm/res/hybrid-compute/license:' = { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // Required parameters "name": { "value": "hclwaf001" - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -146,10 +127,7 @@ module license 'br/public:avm/res/hybrid-compute/license:' = { ```bicep-params using 'br/public:avm/res/hybrid-compute/license:' -// Required parameters param name = 'hclwaf001' -// Non-required parameters -param location = '' ``` diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep index 3879ef9287..84c38955ab 100644 --- a/avm/res/hybrid-compute/license/main.bicep +++ b/avm/res/hybrid-compute/license/main.bicep @@ -94,6 +94,8 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableT } } +@export() +@description('The type for a volume license detail.') type volumeLicenseDetailType = { @description('Required. The invoice id for the volume license.') invoiceId: string diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index 2aa99521a0..cbdfd6ba7f 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "4063370187688285408" + "templateHash": "11101073144149258419" }, "name": "Azure Arc License", "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." @@ -32,6 +32,10 @@ "description": "Required. Describes the program year the volume license is for." } } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a volume license detail." } } }, diff --git a/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep index e601f8aaec..6432ca4e6d 100644 --- a/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep +++ b/avm/res/hybrid-compute/license/tests/e2e/defaults/main.test.bicep @@ -9,14 +9,12 @@ metadata description = 'This instance deploys the module with the minimum set of @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.license-${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.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'hclmin' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -44,7 +42,6 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation } } ] diff --git a/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep index f205cc7f9d..e5ac1a8a02 100644 --- a/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/hybrid-compute/license/tests/e2e/waf-aligned/main.test.bicep @@ -9,14 +9,12 @@ metadata description = 'This instance deploys the module in alignment with the b @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.license-${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.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'hclwaf' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -43,9 +41,7 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - // You parameters go here name: '${namePrefix}${serviceShort}001' - location: resourceLocation } } ] From dc229a3a8fc718e0257d0a8710af93915acd0441 Mon Sep 17 00:00:00 2001 From: Sandy <16922860+thecsw@users.noreply.github.com> Date: Sat, 8 Feb 2025 03:50:21 -0600 Subject: [PATCH 07/53] feat: update `avm/res/storage/storage-account` - Added Defender Plan (Advanced Threat Prodection) (#4379) ## Description Similar to #4363 to enable Microsoft Defender's Advanced Threat Protection plan on storage accounts. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/thecsw/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg)](https://github.com/thecsw/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [X] Azure Verified Module updates: - [ ] 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. - [X] 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 --------- Signed-off-by: Sandy Urazayev --- avm/res/storage/storage-account/README.md | 10 +++ .../container/immutability-policy/main.json | 4 +- .../blob-service/container/main.json | 8 +-- .../storage-account/blob-service/main.json | 12 ++-- .../storage-account/file-service/main.json | 8 +-- .../file-service/share/main.json | 4 +- .../storage-account/local-user/main.json | 4 +- avm/res/storage/storage-account/main.bicep | 12 ++++ avm/res/storage/storage-account/main.json | 72 ++++++++++++------- .../management-policy/main.json | 4 +- .../storage-account/queue-service/main.json | 8 +-- .../queue-service/queue/main.json | 4 +- .../storage-account/table-service/main.json | 8 +-- .../table-service/table/main.json | 4 +- avm/res/storage/storage-account/version.json | 4 +- 15 files changed, 104 insertions(+), 62 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 82b01bb576..94e576af3b 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -22,6 +22,7 @@ This module deploys a Storage Account. | `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) | +| `Microsoft.Security/advancedThreatProtectionSettings` | [2019-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Security/2019-01-01/advancedThreatProtectionSettings) | | `Microsoft.Storage/storageAccounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-05-01/storageAccounts) | | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | @@ -3428,6 +3429,7 @@ param tags = { | [`defaultToOAuthAuthentication`](#parameter-defaulttooauthauthentication) | bool | A boolean flag which indicates whether the default authentication is OAuth or not. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`dnsEndpointType`](#parameter-dnsendpointtype) | string | Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier. | +| [`enableAdvancedThreatProtection`](#parameter-enableadvancedthreatprotection) | bool | Enables Advanced Threat Protection on the storage account. | | [`enableNfsV3`](#parameter-enablenfsv3) | bool | If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true. | | [`enableSftp`](#parameter-enablesftp) | bool | If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | @@ -3786,6 +3788,14 @@ Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a ] ``` +### Parameter: `enableAdvancedThreatProtection` + +Enables Advanced Threat Protection on the storage account. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `enableNfsV3` If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true. diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index dbbe33ca7c..3e1f62c7b5 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index 49e3b47170..df35c3db1f 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -294,8 +294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index b37a944429..23d1aadc51 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7416701536235015086" + "version": "0.33.93.31351", + "templateHash": "7588078546699808778" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -472,8 +472,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -761,8 +761,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 95c9c54fe5..8e27cf79fc 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16196407713115246323" + "version": "0.33.93.31351", + "templateHash": "3168394810831105529" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -359,8 +359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.93.31351", + "templateHash": "12044655551245282190" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index a95097e146..0144ab364d 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.93.31351", + "templateHash": "12044655551245282190" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index dac871c0e5..7be0e3b8da 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16427795222629898111" + "version": "0.33.93.31351", + "templateHash": "5655292159520921149" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 91ee043a7e..8ee2ba44da 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -188,6 +188,9 @@ param keyType string? @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') param secretsExportConfiguration secretsExportConfigurationType? +@description('Optional. Enables Advanced Threat Protection on the storage account.') +param enableAdvancedThreatProtection bool = true + var supportsBlobService = kind == 'BlockBlobStorage' || kind == 'BlobStorage' || kind == 'StorageV2' || kind == 'Storage' var supportsFileService = kind == 'FileStorage' || kind == 'StorageV2' || kind == 'Storage' @@ -694,6 +697,15 @@ module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfigura } } +// Microsoft Defender plan +resource storageAccount_atp 'Microsoft.Security/advancedThreatProtectionSettings@2019-01-01' = if (enableAdvancedThreatProtection) { + name: 'current' + scope: storageAccount + properties: { + isEnabled: true + } +} + @description('The resource ID of the deployed storage account.') output resourceId string = storageAccount.id diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index a6425011ad..a154a8304e 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9739064632358098891" + "version": "0.33.93.31351", + "templateHash": "9582256320776207177" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -1245,6 +1245,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "enableAdvancedThreatProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enables Advanced Threat Protection on the storage account." + } } }, "variables": { @@ -1443,6 +1450,19 @@ "storageAccount" ] }, + "storageAccount_atp": { + "condition": "[parameters('enableAdvancedThreatProtection')]", + "type": "Microsoft.Security/advancedThreatProtectionSettings", + "apiVersion": "2019-01-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "current", + "properties": { + "isEnabled": true + }, + "dependsOn": [ + "storageAccount" + ] + }, "storageAccount_privateEndpoints": { "copy": { "name": "storageAccount_privateEndpoints", @@ -2222,8 +2242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4014848332192190169" + "version": "0.33.93.31351", + "templateHash": "10504956743360699891" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." @@ -2331,8 +2351,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16427795222629898111" + "version": "0.33.93.31351", + "templateHash": "5655292159520921149" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." @@ -2569,8 +2589,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7416701536235015086" + "version": "0.33.93.31351", + "templateHash": "7588078546699808778" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -3036,8 +3056,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -3325,8 +3345,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." @@ -3505,8 +3525,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16196407713115246323" + "version": "0.33.93.31351", + "templateHash": "3168394810831105529" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -3859,8 +3879,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.93.31351", + "templateHash": "12044655551245282190" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." @@ -4294,8 +4314,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14497929042813606497" + "version": "0.33.93.31351", + "templateHash": "1736438454543575457" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -4613,8 +4633,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.93.31351", + "templateHash": "6383154227554431205" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." @@ -4883,8 +4903,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4194630585059896468" + "version": "0.33.93.31351", + "templateHash": "12583903411447171294" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -5199,8 +5219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.93.31351", + "templateHash": "1369356397929898951" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." @@ -5453,8 +5473,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9771994149501143078" + "version": "0.33.93.31351", + "templateHash": "2275047425860597278" } }, "definitions": { diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index bc2c2530f7..38c4ba5749 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4014848332192190169" + "version": "0.33.93.31351", + "templateHash": "10504956743360699891" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index fa4e1c19d4..58b8972173 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14497929042813606497" + "version": "0.33.93.31351", + "templateHash": "1736438454543575457" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -324,8 +324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.93.31351", + "templateHash": "6383154227554431205" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 19308bf371..dfd0124351 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.93.31351", + "templateHash": "6383154227554431205" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index c336dcaed3..791d9ecdc8 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4194630585059896468" + "version": "0.33.93.31351", + "templateHash": "12583903411447171294" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -321,8 +321,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.93.31351", + "templateHash": "1369356397929898951" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index f9e2ac6377..d97ce4a5f1 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.93.31351", + "templateHash": "1369356397929898951" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 8f0ecca899..77443210ac 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.17", + "version": "0.18", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 7c4a386cea1dede2e1e929d65c89178adcf9be84 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Sat, 8 Feb 2025 18:04:13 +0100 Subject: [PATCH 08/53] feat: Updates of `avm/res/network/bastion-host` (#4394) ## Description - added support for the `Developer` SKU - leveraging AVM Common Types - updated cross module reference to the newest version of the Public IP module - updated api versions Resolves #4129 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.bastion-host](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml/badge.svg?branch=users%2Fkrbar%2FbastionUDT)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] 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. - [x] 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 --- avm/res/network/bastion-host/README.md | 123 ++- avm/res/network/bastion-host/main.bicep | 159 +--- avm/res/network/bastion-host/main.json | 870 ++++++++++-------- .../tests/e2e/custompip/dependencies.bicep | 2 +- .../tests/e2e/custompip/main.test.bicep | 6 +- .../tests/e2e/defaults/dependencies.bicep | 2 +- .../tests/e2e/defaults/main.test.bicep | 5 +- .../tests/e2e/developer/dependencies.bicep | 30 + .../tests/e2e/developer/main.test.bicep | 59 ++ .../tests/e2e/max/dependencies.bicep | 4 +- .../tests/e2e/max/main.test.bicep | 6 +- .../tests/e2e/private/dependencies.bicep | 2 +- .../tests/e2e/private/main.test.bicep | 2 +- .../tests/e2e/waf-aligned/dependencies.bicep | 4 +- .../tests/e2e/waf-aligned/main.test.bicep | 6 +- avm/res/network/bastion-host/version.json | 2 +- 16 files changed, 723 insertions(+), 559 deletions(-) create mode 100644 avm/res/network/bastion-host/tests/e2e/developer/dependencies.bicep create mode 100644 avm/res/network/bastion-host/tests/e2e/developer/main.test.bicep diff --git a/avm/res/network/bastion-host/README.md b/avm/res/network/bastion-host/README.md index d0358319a5..5f23a61e2c 100644 --- a/avm/res/network/bastion-host/README.md +++ b/avm/res/network/bastion-host/README.md @@ -18,8 +18,8 @@ This module deploys a Bastion Host. | `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.Network/bastionHosts` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2024-01-01/bastionHosts) | -| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/bastionHosts` | [2024-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2024-05-01/bastionHosts) | +| `Microsoft.Network/publicIPAddresses` | [2024-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2024-05-01/publicIPAddresses) | ## Usage examples @@ -31,9 +31,10 @@ The following section provides usage examples for the module, which were used to - [With a custom public IP address deployed by the module](#example-1-with-a-custom-public-ip-address-deployed-by-the-module) - [Using only defaults](#example-2-using-only-defaults) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [Private-only deployment](#example-4-private-only-deployment) -- [WAF-aligned](#example-5-waf-aligned) +- [Using Developer SKU](#example-3-using-developer-sku) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [Private-only deployment](#example-5-private-only-deployment) +- [WAF-aligned](#example-6-waf-aligned) ### Example 1: _With a custom public IP address deployed by the module_ @@ -289,7 +290,81 @@ param location = ''

-### Example 3: _Using large parameter set_ +### Example 3: _Using Developer SKU_ + +This instance deploys the module with the Developer SKU. + + +

+ +via Bicep module + +```bicep +module bastionHost 'br/public:avm/res/network/bastion-host:' = { + name: 'bastionHostDeployment' + params: { + // Required parameters + name: 'nbhdev001' + virtualNetworkResourceId: '' + // Non-required parameters + location: '' + skuName: 'Developer' + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nbhdev001" + }, + "virtualNetworkResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "skuName": { + "value": "Developer" + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/network/bastion-host:' + +// Required parameters +param name = 'nbhdev001' +param virtualNetworkResourceId = '' +// Non-required parameters +param location = '' +param skuName = 'Developer' +``` + +
+

+ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -529,7 +604,7 @@ param zones = [

-### Example 4: _Private-only deployment_ +### Example 5: _Private-only deployment_ This instance deploys the module as private-only Bastion deployment. @@ -613,7 +688,7 @@ param skuName = 'Premium'

-### Example 5: _WAF-aligned_ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -778,22 +853,22 @@ param tags = { | :-- | :-- | :-- | | [`bastionSubnetPublicIpResourceId`](#parameter-bastionsubnetpublicipresourceid) | string | The Public IP resource ID to associate to the azureBastionSubnet. If empty, then the Public IP that is created as part of this module will be applied to the azureBastionSubnet. This parameter is ignored when enablePrivateOnlyBastion is true. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | -| [`disableCopyPaste`](#parameter-disablecopypaste) | bool | Choose to disable or enable Copy Paste. For Basic SKU Copy/Paste is always enabled. | -| [`enableFileCopy`](#parameter-enablefilecopy) | bool | Choose to disable or enable File Copy. Not supported for Basic SKU. | -| [`enableIpConnect`](#parameter-enableipconnect) | bool | Choose to disable or enable IP Connect. Not supported for Basic SKU. | -| [`enableKerberos`](#parameter-enablekerberos) | bool | Choose to disable or enable Kerberos authentication. | +| [`disableCopyPaste`](#parameter-disablecopypaste) | bool | Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled. | +| [`enableFileCopy`](#parameter-enablefilecopy) | bool | Choose to disable or enable File Copy. Not supported for Basic and Developer SKU. | +| [`enableIpConnect`](#parameter-enableipconnect) | bool | Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU. | +| [`enableKerberos`](#parameter-enablekerberos) | bool | Choose to disable or enable Kerberos authentication. Not supported for Developer SKU. | | [`enablePrivateOnlyBastion`](#parameter-enableprivateonlybastion) | bool | Choose to disable or enable Private-only Bastion deployment. The Premium SKU is required for this feature. | | [`enableSessionRecording`](#parameter-enablesessionrecording) | bool | Choose to disable or enable Session Recording feature. The Premium SKU is required for this feature. If Session Recording is enabled, the Native client support will be disabled. | -| [`enableShareableLink`](#parameter-enableshareablelink) | bool | Choose to disable or enable Shareable Link. Not supported for Basic SKU. | +| [`enableShareableLink`](#parameter-enableshareablelink) | bool | Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`publicIPAddressObject`](#parameter-publicipaddressobject) | object | Specifies the properties of the Public IP to create and be used by Azure Bastion, if no existing public IP was provided. This parameter is ignored when enablePrivateOnlyBastion is true. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | -| [`scaleUnits`](#parameter-scaleunits) | int | The scale units for the Bastion Host resource. The Basic SKU only supports 2 scale units. | +| [`scaleUnits`](#parameter-scaleunits) | int | The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units. | | [`skuName`](#parameter-skuname) | string | The SKU of this Bastion Host. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the Bastion Host resource needs to come from. | +| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU. | ### Parameter: `name` @@ -931,7 +1006,7 @@ Resource ID of the diagnostic log analytics workspace. For security reasons, it ### Parameter: `disableCopyPaste` -Choose to disable or enable Copy Paste. For Basic SKU Copy/Paste is always enabled. +Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled. - Required: No - Type: bool @@ -939,7 +1014,7 @@ Choose to disable or enable Copy Paste. For Basic SKU Copy/Paste is always enabl ### Parameter: `enableFileCopy` -Choose to disable or enable File Copy. Not supported for Basic SKU. +Choose to disable or enable File Copy. Not supported for Basic and Developer SKU. - Required: No - Type: bool @@ -947,7 +1022,7 @@ Choose to disable or enable File Copy. Not supported for Basic SKU. ### Parameter: `enableIpConnect` -Choose to disable or enable IP Connect. Not supported for Basic SKU. +Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU. - Required: No - Type: bool @@ -955,7 +1030,7 @@ Choose to disable or enable IP Connect. Not supported for Basic SKU. ### Parameter: `enableKerberos` -Choose to disable or enable Kerberos authentication. +Choose to disable or enable Kerberos authentication. Not supported for Developer SKU. - Required: No - Type: bool @@ -979,7 +1054,7 @@ Choose to disable or enable Session Recording feature. The Premium SKU is requir ### Parameter: `enableShareableLink` -Choose to disable or enable Shareable Link. Not supported for Basic SKU. +Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU. - Required: No - Type: bool @@ -1155,7 +1230,7 @@ The principal type of the assigned principal ID. ### Parameter: `scaleUnits` -The scale units for the Bastion Host resource. The Basic SKU only supports 2 scale units. +The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units. - Required: No - Type: int @@ -1172,6 +1247,7 @@ The SKU of this Bastion Host. ```Bicep [ 'Basic' + 'Developer' 'Premium' 'Standard' ] @@ -1186,7 +1262,7 @@ Tags of the resource. ### Parameter: `zones` -A list of availability zones denoting where the Bastion Host resource needs to come from. +A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU. - Required: No - Type: array @@ -1216,7 +1292,8 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/public-ip-address:0.6.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.8.0` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/network/bastion-host/main.bicep b/avm/res/network/bastion-host/main.bicep index 3048d66789..70c415b18b 100644 --- a/avm/res/network/bastion-host/main.bicep +++ b/avm/res/network/bastion-host/main.bicep @@ -18,33 +18,36 @@ param publicIPAddressObject object = { name: '${name}-pip' } +import { diagnosticSettingLogsOnlyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The diagnostic settings of the service.') -param diagnosticSettings diagnosticSettingType +param diagnosticSettings diagnosticSettingLogsOnlyType[]? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? @allowed([ 'Basic' + 'Developer' 'Premium' 'Standard' ]) @description('Optional. The SKU of this Bastion Host.') param skuName string = 'Basic' -@description('Optional. Choose to disable or enable Copy Paste. For Basic SKU Copy/Paste is always enabled.') +@description('Optional. Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled.') param disableCopyPaste bool = false -@description('Optional. Choose to disable or enable File Copy. Not supported for Basic SKU.') +@description('Optional. Choose to disable or enable File Copy. Not supported for Basic and Developer SKU.') param enableFileCopy bool = true -@description('Optional. Choose to disable or enable IP Connect. Not supported for Basic SKU.') +@description('Optional. Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU.') param enableIpConnect bool = false -@description('Optional. Choose to disable or enable Kerberos authentication.') +@description('Optional. Choose to disable or enable Kerberos authentication. Not supported for Developer SKU.') param enableKerberos bool = false -@description('Optional. Choose to disable or enable Shareable Link. Not supported for Basic SKU.') +@description('Optional. Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU.') param enableShareableLink bool = false @description('Optional. Choose to disable or enable Session Recording feature. The Premium SKU is required for this feature. If Session Recording is enabled, the Native client support will be disabled.') @@ -53,11 +56,12 @@ param enableSessionRecording bool = false @description('Optional. Choose to disable or enable Private-only Bastion deployment. The Premium SKU is required for this feature.') param enablePrivateOnlyBastion bool = false -@description('Optional. The scale units for the Bastion Host resource. The Basic SKU only supports 2 scale units.') +@description('Optional. The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units.') param scaleUnits int = 2 +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? @description('Optional. Tags of the resource.') param tags object? @@ -65,7 +69,7 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true -@description('Optional. A list of availability zones denoting where the Bastion Host resource needs to come from.') +@description('Optional. A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU.') @allowed([ 1 2 @@ -77,28 +81,31 @@ param zones int[] = [] // Availability Zones are currently in preview and only a // Prep ipConfigurations object AzureBastionSubnet for different uses cases: // 1. Use existing Public IP // 2. Use new Public IP created in this module -var ipConfigurations = [ - { - name: 'IpConfAzureBastionSubnet' - properties: union( +// (skuName == 'Developer' is a special case where ipConfigurations is empty) +var ipConfigurations = skuName == 'Developer' + ? [] + : [ { - subnet: { - id: '${virtualNetworkResourceId}/subnets/AzureBastionSubnet' // The subnet name must be AzureBastionSubnet - } - }, - (!enablePrivateOnlyBastion - ? { - //Use existing Public IP, new Public IP created in this module - publicIPAddress: { - id: !empty(bastionSubnetPublicIpResourceId) - ? bastionSubnetPublicIpResourceId - : publicIPAddress.outputs.resourceId + name: 'IpConfAzureBastionSubnet' + properties: union( + { + subnet: { + id: '${virtualNetworkResourceId}/subnets/AzureBastionSubnet' // The subnet name must be AzureBastionSubnet } - } - : {}) - ) - } -] + }, + (!enablePrivateOnlyBastion + ? { + //Use existing Public IP, new Public IP created in this module + publicIPAddress: { + id: !empty(bastionSubnetPublicIpResourceId) + ? bastionSubnetPublicIpResourceId + : publicIPAddress.outputs.resourceId + } + } + : {}) + ) + } + ] // ---------------------------------------------------------------------------- @@ -146,7 +153,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.6.0' = if (empty(bastionSubnetPublicIpResourceId) && (!enablePrivateOnlyBastion)) { +module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.8.0' = if (empty(bastionSubnetPublicIpResourceId) && (skuName != 'Developer') && (!enablePrivateOnlyBastion)) { name: '${uniqueString(deployment().name, location)}-Bastion-PIP' params: { name: publicIPAddressObject.name @@ -167,9 +174,16 @@ module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.6.0' = if var bastionpropertiesVar = union( { - scaleUnits: skuName == 'Basic' ? 2 : scaleUnits + scaleUnits: (skuName == 'Basic' || skuName == 'Developer') ? 2 : scaleUnits ipConfigurations: ipConfigurations }, + (skuName == 'Developer' + ? { + virtualNetwork: { + id: virtualNetworkResourceId + } + } + : {}), ((skuName == 'Basic' || skuName == 'Standard' || skuName == 'Premium') ? { enableKerberos: enableKerberos @@ -192,14 +206,14 @@ var bastionpropertiesVar = union( : {}) ) -resource azureBastion 'Microsoft.Network/bastionHosts@2024-01-01' = { +resource azureBastion 'Microsoft.Network/bastionHosts@2024-05-01' = { name: name location: location - tags: tags + tags: tags ?? {} // The empty object is a workaround for error when deploying with the Developer SKU. The error seems unrelated to the tags, but it is resolved by adding the empty object. sku: { name: skuName } - zones: map(zones, zone => string(zone)) + zones: skuName == 'Developer' ? [] : map(zones, zone => string(zone)) properties: bastionpropertiesVar } @@ -265,77 +279,4 @@ output resourceId string = azureBastion.id output location string = azureBastion.location @description('The Public IPconfiguration object for the AzureBastionSubnet.') -output ipConfAzureBastionSubnet object = azureBastion.properties.ipConfigurations[0] - -// =============== // -// Definitions // -// =============== // - -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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 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. Enable or disable the category explicitly. Default is `true`.') - enabled: bool? - }[]? - - @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')? - - @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? -}[]? +output ipConfAzureBastionSubnet object = skuName == 'Developer' ? {} : azureBastion.properties.ipConfigurations[0] diff --git a/avm/res/network/bastion-host/main.json b/avm/res/network/bastion-host/main.json index ecfca15cc8..27dfd312d4 100644 --- a/avm/res/network/bastion-host/main.json +++ b/avm/res/network/bastion-host/main.json @@ -5,13 +5,110 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4032958951426322911" + "version": "0.33.93.31351", + "templateHash": "16499979971476585213" }, "name": "Bastion Hosts", "description": "This module deploys a Bastion Host." }, "definitions": { + "diagnosticSettingLogsOnlyType": { + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, "lockType": { "type": "object", "properties": { @@ -35,175 +132,87 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } - } - }, - "nullable": true - }, - "diagnosticSettingType": { - "type": "array", - "items": { - "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." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } - } - }, - "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." - } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -243,13 +252,18 @@ } }, "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, "metadata": { "description": "Optional. The diagnostic settings of the service." } }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -259,6 +273,7 @@ "defaultValue": "Basic", "allowedValues": [ "Basic", + "Developer", "Premium", "Standard" ], @@ -270,35 +285,35 @@ "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Choose to disable or enable Copy Paste. For Basic SKU Copy/Paste is always enabled." + "description": "Optional. Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled." } }, "enableFileCopy": { "type": "bool", "defaultValue": true, "metadata": { - "description": "Optional. Choose to disable or enable File Copy. Not supported for Basic SKU." + "description": "Optional. Choose to disable or enable File Copy. Not supported for Basic and Developer SKU." } }, "enableIpConnect": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Choose to disable or enable IP Connect. Not supported for Basic SKU." + "description": "Optional. Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU." } }, "enableKerberos": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Choose to disable or enable Kerberos authentication." + "description": "Optional. Choose to disable or enable Kerberos authentication. Not supported for Developer SKU." } }, "enableShareableLink": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Choose to disable or enable Shareable Link. Not supported for Basic SKU." + "description": "Optional. Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU." } }, "enableSessionRecording": { @@ -319,11 +334,15 @@ "type": "int", "defaultValue": 2, "metadata": { - "description": "Optional. The scale units for the Bastion Host resource. The Basic SKU only supports 2 scale units." + "description": "Optional. The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -354,7 +373,7 @@ 3 ], "metadata": { - "description": "Optional. A list of availability zones denoting where the Bastion Host resource needs to come from." + "description": "Optional. A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU." } } }, @@ -397,15 +416,15 @@ }, "azureBastion": { "type": "Microsoft.Network/bastionHosts", - "apiVersion": "2024-01-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "tags": "[coalesce(parameters('tags'), createObject())]", "sku": { "name": "[parameters('skuName')]" }, - "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", - "properties": "[union(createObject('scaleUnits', if(equals(parameters('skuName'), 'Basic'), 2, parameters('scaleUnits')), 'ipConfigurations', createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), if(not(parameters('enablePrivateOnlyBastion')), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value))), createObject()))))), if(or(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Standard')), equals(parameters('skuName'), 'Premium')), createObject('enableKerberos', parameters('enableKerberos')), createObject()), if(or(equals(parameters('skuName'), 'Standard'), equals(parameters('skuName'), 'Premium')), createObject('enableTunneling', if(equals(parameters('skuName'), 'Standard'), true(), if(parameters('enableSessionRecording'), false(), true())), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()), if(equals(parameters('skuName'), 'Premium'), createObject('enableSessionRecording', parameters('enableSessionRecording'), 'enablePrivateOnlyBastion', parameters('enablePrivateOnlyBastion')), createObject()))]", + "zones": "[if(equals(parameters('skuName'), 'Developer'), createArray(), map(parameters('zones'), lambda('zone', string(lambdaVariables('zone')))))]", + "properties": "[union(createObject('scaleUnits', if(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Developer')), 2, parameters('scaleUnits')), 'ipConfigurations', if(equals(parameters('skuName'), 'Developer'), createArray(), createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), if(not(parameters('enablePrivateOnlyBastion')), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))))), if(equals(parameters('skuName'), 'Developer'), createObject('virtualNetwork', createObject('id', parameters('virtualNetworkResourceId'))), createObject()), if(or(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Standard')), equals(parameters('skuName'), 'Premium')), createObject('enableKerberos', parameters('enableKerberos')), createObject()), if(or(equals(parameters('skuName'), 'Standard'), equals(parameters('skuName'), 'Premium')), createObject('enableTunneling', if(equals(parameters('skuName'), 'Standard'), true(), if(parameters('enableSessionRecording'), false(), true())), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()), if(equals(parameters('skuName'), 'Premium'), createObject('enableSessionRecording', parameters('enableSessionRecording'), 'enablePrivateOnlyBastion', parameters('enablePrivateOnlyBastion')), createObject()))]", "dependsOn": [ "publicIPAddress" ] @@ -479,7 +498,7 @@ ] }, "publicIPAddress": { - "condition": "[and(empty(parameters('bastionSubnetPublicIpResourceId')), not(parameters('enablePrivateOnlyBastion')))]", + "condition": "[and(and(empty(parameters('bastionSubnetPublicIpResourceId')), not(equals(parameters('skuName'), 'Developer'))), not(parameters('enablePrivateOnlyBastion')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", @@ -536,112 +555,13 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16693645977675862540" + "version": "0.33.93.31351", + "templateHash": "5168739580767459761" }, "name": "Public IP Addresses", - "description": "This module deploys a Public IP Address.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Public IP Address." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } - } - } - }, - "nullable": true - }, - "lockType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, "dnsSettingsType": { "type": "object", "properties": { @@ -654,14 +574,14 @@ "domainNameLabelScope": { "type": "string", "allowedValues": [ - "", "NoReuse", "ResourceGroupReuse", "SubscriptionReuse", "TenantReuse" ], + "nullable": true, "metadata": { - "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." } }, "fqdn": { @@ -678,6 +598,9 @@ "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." } } + }, + "metadata": { + "__bicep_export!": true } }, "ddosSettingsType": { @@ -707,127 +630,257 @@ "description": "Required. The DDoS protection policy customizations." } } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipTagType": { + "type": "object", + "properties": { + "ipTagType": { + "type": "string", + "metadata": { + "description": "Required. The IP tag type." + } + }, + "tag": { + "type": "string", + "metadata": { + "description": "Required. The IP tag." + } + } + }, + "metadata": { + "__bicep_export!": true } }, - "diagnosticSettingType": { - "type": "array", - "items": { - "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." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the 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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } - }, - "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." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric 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." - } + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric 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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } } }, "parameters": { @@ -892,8 +945,19 @@ "description": "Optional. The DNS settings of the public IP address." } }, + "ipTags": { + "type": "array", + "items": { + "$ref": "#/definitions/ipTagType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of tags associated with the public IP address." + } + }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -935,7 +999,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -962,7 +1030,11 @@ } }, "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { "description": "Optional. The diagnostic settings of the service." } @@ -994,7 +1066,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.6.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1012,7 +1084,7 @@ }, "publicIpAddress": { "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-09-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1028,7 +1100,7 @@ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", - "ipTags": null + "ipTags": "[parameters('ipTags')]" } }, "publicIpAddress_lock": { @@ -1143,7 +1215,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + "value": "[reference('publicIpAddress', '2024-05-01', 'full').location]" } } } @@ -1177,14 +1249,14 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('azureBastion', '2024-01-01', 'full').location]" + "value": "[reference('azureBastion', '2024-05-01', 'full').location]" }, "ipConfAzureBastionSubnet": { "type": "object", "metadata": { "description": "The Public IPconfiguration object for the AzureBastionSubnet." }, - "value": "[reference('azureBastion').ipConfigurations[0]]" + "value": "[if(equals(parameters('skuName'), 'Developer'), createObject(), reference('azureBastion').ipConfigurations[0])]" } } } \ No newline at end of file diff --git a/avm/res/network/bastion-host/tests/e2e/custompip/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/custompip/dependencies.bicep index 4166c9be72..e4b1080880 100644 --- a/avm/res/network/bastion-host/tests/e2e/custompip/dependencies.bicep +++ b/avm/res/network/bastion-host/tests/e2e/custompip/dependencies.bicep @@ -9,7 +9,7 @@ param managedIdentityName string var addressPrefix = '10.0.0.0/16' -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { diff --git a/avm/res/network/bastion-host/tests/e2e/custompip/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/custompip/main.test.bicep index ca63db4a59..13e17f8de0 100644 --- a/avm/res/network/bastion-host/tests/e2e/custompip/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/custompip/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -107,9 +107,5 @@ module testDeployment '../../../main.bicep' = [ } } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/network/bastion-host/tests/e2e/defaults/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/defaults/dependencies.bicep index 7aada15d55..e7f1953da8 100644 --- a/avm/res/network/bastion-host/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/network/bastion-host/tests/e2e/defaults/dependencies.bicep @@ -6,7 +6,7 @@ param virtualNetworkName string var addressPrefix = '10.0.0.0/16' -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { diff --git a/avm/res/network/bastion-host/tests/e2e/defaults/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/defaults/main.test.bicep index 19706f01fd..b87f5575f8 100644 --- a/avm/res/network/bastion-host/tests/e2e/defaults/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/defaults/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -54,8 +54,5 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/bastion-host/tests/e2e/developer/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/developer/dependencies.bicep new file mode 100644 index 0000000000..e7f1953da8 --- /dev/null +++ b/avm/res/network/bastion-host/tests/e2e/developer/dependencies.bicep @@ -0,0 +1,30 @@ +@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@2024-05-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/avm/res/network/bastion-host/tests/e2e/developer/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/developer/main.test.bicep new file mode 100644 index 0000000000..a62c4e437b --- /dev/null +++ b/avm/res/network/bastion-host/tests/e2e/developer/main.test.bicep @@ -0,0 +1,59 @@ +targetScope = 'subscription' + +metadata name = 'Using Developer SKU' +metadata description = 'This instance deploys the module with the Developer SKU.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.bastionhosts-${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 = 'nbhdev' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + 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' + skuName: 'Developer' + location: resourceLocation + virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + } + } +] diff --git a/avm/res/network/bastion-host/tests/e2e/max/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/max/dependencies.bicep index 1ff59b7834..d5fdc86712 100644 --- a/avm/res/network/bastion-host/tests/e2e/max/dependencies.bicep +++ b/avm/res/network/bastion-host/tests/e2e/max/dependencies.bicep @@ -12,7 +12,7 @@ param managedIdentityName string var addressPrefix = '10.0.0.0/16' -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { @@ -32,7 +32,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { } } -resource publicIP 'Microsoft.Network/publicIPAddresses@2024-01-01' = { +resource publicIP 'Microsoft.Network/publicIPAddresses@2024-05-01' = { name: publicIPName location: location sku: { diff --git a/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep index 2b78c0cf46..10ede671ff 100644 --- a/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep @@ -27,7 +27,7 @@ var enforcedLocation = 'northeurope' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: enforcedLocation } @@ -123,9 +123,5 @@ module testDeployment '../../../main.bicep' = [ 3 ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/network/bastion-host/tests/e2e/private/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/private/dependencies.bicep index 7aada15d55..e7f1953da8 100644 --- a/avm/res/network/bastion-host/tests/e2e/private/dependencies.bicep +++ b/avm/res/network/bastion-host/tests/e2e/private/dependencies.bicep @@ -6,7 +6,7 @@ param virtualNetworkName string var addressPrefix = '10.0.0.0/16' -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { diff --git a/avm/res/network/bastion-host/tests/e2e/private/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/private/main.test.bicep index b35c9f07a9..eee5592b8c 100644 --- a/avm/res/network/bastion-host/tests/e2e/private/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/private/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep index 8027783d25..4691026fae 100644 --- a/avm/res/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/network/bastion-host/tests/e2e/waf-aligned/dependencies.bicep @@ -9,7 +9,7 @@ param publicIPName string var addressPrefix = '10.0.0.0/16' -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { @@ -29,7 +29,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { } } -resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { +resource publicIP 'Microsoft.Network/publicIPAddresses@2024-05-01' = { name: publicIPName location: location sku: { diff --git a/avm/res/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep index 861b40da3d..a4b10faba8 100644 --- a/avm/res/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/waf-aligned/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -90,9 +90,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/network/bastion-host/version.json b/avm/res/network/bastion-host/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/network/bastion-host/version.json +++ b/avm/res/network/bastion-host/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] From 0c5d031c49697cff4103fe21c948c61ff080aad1 Mon Sep 17 00:00:00 2001 From: Sandy <16922860+thecsw@users.noreply.github.com> Date: Sun, 9 Feb 2025 06:40:45 -0600 Subject: [PATCH 09/53] fix: revert Defender updates to `avm/res/storage/storage-account` (#4414) This reverts PR #4379 due to deploy tests failing, > Failed to enable the Defender for Storage (classic) plan on the storage account '***ssablob001' - This plan is no longer available for new subscriptions and storage accounts, subscriptions already using the new or classic per storage account plans, or re-enabling. Please use the latest API to protect your storage account. If you have enabled the new plan, disable any policies attempting to re-enable the classic plan. Thanks to @AlexanderSehr for bringing it out. ## Description ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/thecsw/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=sandy%2Frevert-storage-atp)](https://github.com/thecsw/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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. - [X] 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 --- avm/res/storage/storage-account/README.md | 10 --- .../container/immutability-policy/main.json | 4 +- .../blob-service/container/main.json | 8 +-- .../storage-account/blob-service/main.json | 12 ++-- .../storage-account/file-service/main.json | 8 +-- .../file-service/share/main.json | 4 +- .../storage-account/local-user/main.json | 4 +- avm/res/storage/storage-account/main.bicep | 12 ---- avm/res/storage/storage-account/main.json | 72 +++++++------------ .../management-policy/main.json | 4 +- .../storage-account/queue-service/main.json | 8 +-- .../queue-service/queue/main.json | 4 +- .../storage-account/table-service/main.json | 8 +-- .../table-service/table/main.json | 4 +- avm/res/storage/storage-account/version.json | 4 +- 15 files changed, 62 insertions(+), 104 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 94e576af3b..82b01bb576 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -22,7 +22,6 @@ This module deploys a Storage Account. | `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) | -| `Microsoft.Security/advancedThreatProtectionSettings` | [2019-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Security/2019-01-01/advancedThreatProtectionSettings) | | `Microsoft.Storage/storageAccounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-05-01/storageAccounts) | | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | @@ -3429,7 +3428,6 @@ param tags = { | [`defaultToOAuthAuthentication`](#parameter-defaulttooauthauthentication) | bool | A boolean flag which indicates whether the default authentication is OAuth or not. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`dnsEndpointType`](#parameter-dnsendpointtype) | string | Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier. | -| [`enableAdvancedThreatProtection`](#parameter-enableadvancedthreatprotection) | bool | Enables Advanced Threat Protection on the storage account. | | [`enableNfsV3`](#parameter-enablenfsv3) | bool | If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true. | | [`enableSftp`](#parameter-enablesftp) | bool | If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | @@ -3788,14 +3786,6 @@ Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a ] ``` -### Parameter: `enableAdvancedThreatProtection` - -Enables Advanced Threat Protection on the storage account. - -- Required: No -- Type: bool -- Default: `True` - ### Parameter: `enableNfsV3` If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true. diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index 3e1f62c7b5..dbbe33ca7c 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.32.4.45862", + "templateHash": "12930903258566593173" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index df35c3db1f..49e3b47170 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.32.4.45862", + "templateHash": "7180309977212880563" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -294,8 +294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.32.4.45862", + "templateHash": "12930903258566593173" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 23d1aadc51..b37a944429 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7588078546699808778" + "version": "0.32.4.45862", + "templateHash": "7416701536235015086" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -472,8 +472,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.32.4.45862", + "templateHash": "7180309977212880563" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -761,8 +761,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.32.4.45862", + "templateHash": "12930903258566593173" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 8e27cf79fc..95c9c54fe5 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3168394810831105529" + "version": "0.32.4.45862", + "templateHash": "16196407713115246323" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -359,8 +359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12044655551245282190" + "version": "0.32.4.45862", + "templateHash": "5204319087439022536" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index 0144ab364d..a95097e146 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12044655551245282190" + "version": "0.32.4.45862", + "templateHash": "5204319087439022536" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index 7be0e3b8da..dac871c0e5 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5655292159520921149" + "version": "0.32.4.45862", + "templateHash": "16427795222629898111" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 8ee2ba44da..91ee043a7e 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -188,9 +188,6 @@ param keyType string? @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') param secretsExportConfiguration secretsExportConfigurationType? -@description('Optional. Enables Advanced Threat Protection on the storage account.') -param enableAdvancedThreatProtection bool = true - var supportsBlobService = kind == 'BlockBlobStorage' || kind == 'BlobStorage' || kind == 'StorageV2' || kind == 'Storage' var supportsFileService = kind == 'FileStorage' || kind == 'StorageV2' || kind == 'Storage' @@ -697,15 +694,6 @@ module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfigura } } -// Microsoft Defender plan -resource storageAccount_atp 'Microsoft.Security/advancedThreatProtectionSettings@2019-01-01' = if (enableAdvancedThreatProtection) { - name: 'current' - scope: storageAccount - properties: { - isEnabled: true - } -} - @description('The resource ID of the deployed storage account.') output resourceId string = storageAccount.id diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index a154a8304e..a6425011ad 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "9582256320776207177" + "version": "0.32.4.45862", + "templateHash": "9739064632358098891" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -1245,13 +1245,6 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } - }, - "enableAdvancedThreatProtection": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enables Advanced Threat Protection on the storage account." - } } }, "variables": { @@ -1450,19 +1443,6 @@ "storageAccount" ] }, - "storageAccount_atp": { - "condition": "[parameters('enableAdvancedThreatProtection')]", - "type": "Microsoft.Security/advancedThreatProtectionSettings", - "apiVersion": "2019-01-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", - "name": "current", - "properties": { - "isEnabled": true - }, - "dependsOn": [ - "storageAccount" - ] - }, "storageAccount_privateEndpoints": { "copy": { "name": "storageAccount_privateEndpoints", @@ -2242,8 +2222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10504956743360699891" + "version": "0.32.4.45862", + "templateHash": "4014848332192190169" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." @@ -2351,8 +2331,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5655292159520921149" + "version": "0.32.4.45862", + "templateHash": "16427795222629898111" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." @@ -2589,8 +2569,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7588078546699808778" + "version": "0.32.4.45862", + "templateHash": "7416701536235015086" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -3056,8 +3036,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.32.4.45862", + "templateHash": "7180309977212880563" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -3345,8 +3325,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.32.4.45862", + "templateHash": "12930903258566593173" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." @@ -3525,8 +3505,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3168394810831105529" + "version": "0.32.4.45862", + "templateHash": "16196407713115246323" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -3879,8 +3859,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12044655551245282190" + "version": "0.32.4.45862", + "templateHash": "5204319087439022536" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." @@ -4314,8 +4294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1736438454543575457" + "version": "0.32.4.45862", + "templateHash": "14497929042813606497" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -4633,8 +4613,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6383154227554431205" + "version": "0.32.4.45862", + "templateHash": "9969689246600110741" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." @@ -4903,8 +4883,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12583903411447171294" + "version": "0.32.4.45862", + "templateHash": "4194630585059896468" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -5219,8 +5199,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1369356397929898951" + "version": "0.32.4.45862", + "templateHash": "4457939127962832961" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." @@ -5473,8 +5453,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2275047425860597278" + "version": "0.32.4.45862", + "templateHash": "9771994149501143078" } }, "definitions": { diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index 38c4ba5749..bc2c2530f7 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10504956743360699891" + "version": "0.32.4.45862", + "templateHash": "4014848332192190169" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index 58b8972173..fa4e1c19d4 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1736438454543575457" + "version": "0.32.4.45862", + "templateHash": "14497929042813606497" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -324,8 +324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6383154227554431205" + "version": "0.32.4.45862", + "templateHash": "9969689246600110741" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index dfd0124351..19308bf371 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6383154227554431205" + "version": "0.32.4.45862", + "templateHash": "9969689246600110741" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index 791d9ecdc8..c336dcaed3 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12583903411447171294" + "version": "0.32.4.45862", + "templateHash": "4194630585059896468" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -321,8 +321,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1369356397929898951" + "version": "0.32.4.45862", + "templateHash": "4457939127962832961" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index d97ce4a5f1..f9e2ac6377 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1369356397929898951" + "version": "0.32.4.45862", + "templateHash": "4457939127962832961" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 77443210ac..8f0ecca899 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.18", + "version": "0.17", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file From c016996ac8ccd16f1f4440e51a9ec7b063d3da0c Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Sun, 9 Feb 2025 17:46:42 +0000 Subject: [PATCH 10/53] fix: avm/res/storage/storage-account blob soft delete days to 7 (#4411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description I flagged last year that the blob soft delete days for blob was only 6, but for containers it was 7. The module owner agreed for consistency it should be 7 also. Therefore, this PR bumps this in line as per #2707.  Closes #2707 to increase blob soft delete inline to 7 days with containers for consistency. ## Pipeline Reference [![avm.res.storage.storage-account](https://github.com/riosengineer/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg)](https://github.com/riosengineer/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] 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 --- .../storage-account/blob-service/README.md | 2 +- .../container/immutability-policy/main.json | 4 +- .../blob-service/container/main.json | 8 +-- .../storage-account/blob-service/main.bicep | 2 +- .../storage-account/blob-service/main.json | 14 ++--- avm/res/storage/storage-account/main.json | 54 +++++++++---------- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/avm/res/storage/storage-account/blob-service/README.md b/avm/res/storage/storage-account/blob-service/README.md index 9d0866eeec..831df721ec 100644 --- a/avm/res/storage/storage-account/blob-service/README.md +++ b/avm/res/storage/storage-account/blob-service/README.md @@ -440,7 +440,7 @@ How long this blob can be restored. It should be less than DeleteRetentionPolicy - Required: No - Type: int -- Default: `6` +- Default: `7` - MinValue: 1 - MaxValue: 365 diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index dbbe33ca7c..3e1f62c7b5 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index 49e3b47170..df35c3db1f 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -294,8 +294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/main.bicep b/avm/res/storage/storage-account/blob-service/main.bicep index e88a222172..3198c5f316 100644 --- a/avm/res/storage/storage-account/blob-service/main.bicep +++ b/avm/res/storage/storage-account/blob-service/main.bicep @@ -55,7 +55,7 @@ param restorePolicyEnabled bool = false @minValue(1) @description('Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days.') -param restorePolicyDays int = 6 +param restorePolicyDays int = 7 @description('Optional. Blob containers to create.') param containers array? diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index b37a944429..4eacd0e9e2 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7416701536235015086" + "version": "0.33.93.31351", + "templateHash": "2058460323623594433" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -312,7 +312,7 @@ }, "restorePolicyDays": { "type": "int", - "defaultValue": 6, + "defaultValue": 7, "minValue": 1, "metadata": { "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." @@ -472,8 +472,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -761,8 +761,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index a6425011ad..b3193cf49f 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9739064632358098891" + "version": "0.33.93.31351", + "templateHash": "17960260729884623690" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -2222,8 +2222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4014848332192190169" + "version": "0.33.93.31351", + "templateHash": "10504956743360699891" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." @@ -2331,8 +2331,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16427795222629898111" + "version": "0.33.93.31351", + "templateHash": "5655292159520921149" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." @@ -2569,8 +2569,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7416701536235015086" + "version": "0.33.93.31351", + "templateHash": "2058460323623594433" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -2876,7 +2876,7 @@ }, "restorePolicyDays": { "type": "int", - "defaultValue": 6, + "defaultValue": 7, "minValue": 1, "metadata": { "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." @@ -3036,8 +3036,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7180309977212880563" + "version": "0.33.93.31351", + "templateHash": "2991444340097371621" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -3325,8 +3325,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12930903258566593173" + "version": "0.33.93.31351", + "templateHash": "8061556339565534458" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." @@ -3505,8 +3505,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16196407713115246323" + "version": "0.33.93.31351", + "templateHash": "3168394810831105529" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -3859,8 +3859,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.93.31351", + "templateHash": "12044655551245282190" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." @@ -4294,8 +4294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14497929042813606497" + "version": "0.33.93.31351", + "templateHash": "1736438454543575457" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -4613,8 +4613,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.93.31351", + "templateHash": "6383154227554431205" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." @@ -4883,8 +4883,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4194630585059896468" + "version": "0.33.93.31351", + "templateHash": "12583903411447171294" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -5199,8 +5199,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.93.31351", + "templateHash": "1369356397929898951" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." @@ -5453,8 +5453,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9771994149501143078" + "version": "0.33.93.31351", + "templateHash": "2275047425860597278" } }, "definitions": { From 70a600e4b295c6161cd8a109163b87f7ec378da1 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 10 Feb 2025 19:49:09 +0100 Subject: [PATCH 11/53] ci: Add `avm/ptn/lz/sub-vending` and `avm/res/azure-stack-hci/cluster` to the list of OIDC exception list (#4393) ## Description hub-spoke test is failing on role assignment removal when leveraging OIDC. Adding it back to the exception list to fall back to SP+secret authentication method, while the issue is being troubleshooted. Same issue with the yet-to-be-published `avm/res/azure-stack-hci/cluster` module tests ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.lz.sub-vending](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml/badge.svg?branch=users%2Ferikag%2Fsublz_oidc_exp&event=workflow_run)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml) | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../actions/templates/avm-validateModuleDeployment/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/templates/avm-validateModuleDeployment/action.yml b/.github/actions/templates/avm-validateModuleDeployment/action.yml index c26b7137e9..f127041711 100644 --- a/.github/actions/templates/avm-validateModuleDeployment/action.yml +++ b/.github/actions/templates/avm-validateModuleDeployment/action.yml @@ -73,6 +73,8 @@ runs: # List of modules requiring service principal + secret authentication $modulePath = "${{ inputs.modulePath }}" $exceptionModulePaths = @( + 'avm/ptn/lz/sub-vending' # Failing on resource deletion when trying to delete RBAC at subscription level (hub-spoke test) + 'avm/res/azure-stack-hci/cluster' # Failing on resource deletion when trying to delete RBAC at subscription level 'avm/res/compute/image' # Failing on resource deletion when trying to delete RBAC at subscription level 'avm/res/compute/disk' # Failing on resource deletion when trying to delete RBAC at subscription level 'avm/ptn/virtual-machine-images/azure-image-builder' # Failing on resource deletion when trying to delete RBAC at subscription level From b417c21a04a6c015ed18ce752e041046f4dbe16e Mon Sep 17 00:00:00 2001 From: Adam Ricket <44428944+thecmdradama@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:52:00 +0800 Subject: [PATCH 12/53] feat: Updates of `avm/res/network/express-route-circuits` (#4400) ## Description - Adds support for an optional list of authorizations. - Adds output for authorizations as a flattened map. - Adds output for the service provider provisioning state property. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.express-route-circuit](https://github.com/thecmdradama/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml/badge.svg)](https://github.com/thecmdradama/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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. - [x] 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`. - [x] 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 --- .../network/express-route-circuit/README.md | 10 +++++++ .../network/express-route-circuit/main.bicep | 11 +++++++ .../network/express-route-circuit/main.json | 30 +++++++++++++++++-- .../express-route-circuit/version.json | 2 +- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/avm/res/network/express-route-circuit/README.md b/avm/res/network/express-route-circuit/README.md index 9f1ad4093b..c8972d64f7 100644 --- a/avm/res/network/express-route-circuit/README.md +++ b/avm/res/network/express-route-circuit/README.md @@ -523,6 +523,7 @@ param tags = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`allowClassicOperations`](#parameter-allowclassicoperations) | bool | Allow classic operations. You can connect to virtual networks in the classic deployment model by setting allowClassicOperations to true. | +| [`authorizationNames`](#parameter-authorizationnames) | array | List of names for ExpressRoute circuit authorizations to create. To fetch the `authorizationKey` for the authorization, use the `existing` resource reference for `Microsoft.Network/expressRouteCircuits/authorizations`. | | [`bandwidthInGbps`](#parameter-bandwidthingbps) | int | The bandwidth of the circuit when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. Default value of 0 will set the property to null. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | @@ -578,6 +579,14 @@ Allow classic operations. You can connect to virtual networks in the classic dep - Type: bool - Default: `False` +### Parameter: `authorizationNames` + +List of names for ExpressRoute circuit authorizations to create. To fetch the `authorizationKey` for the authorization, use the `existing` resource reference for `Microsoft.Network/expressRouteCircuits/authorizations`. + +- Required: No +- Type: array +- Default: `[]` + ### Parameter: `bandwidthInGbps` The bandwidth of the circuit when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. Default value of 0 will set the property to null. @@ -1014,6 +1023,7 @@ Specifies the identifier that is used to identify the customer. | `resourceGroupName` | string | The resource group the express route curcuit was deployed into. | | `resourceId` | string | The resource ID of express route curcuit. | | `serviceKey` | string | The service key of the express route circuit. | +| `serviceProviderProvisioningState` | string | The service provider provisioning state of the express route circuit. | ## Data Collection diff --git a/avm/res/network/express-route-circuit/main.bicep b/avm/res/network/express-route-circuit/main.bicep index 70094abb9f..6e0332ae4f 100644 --- a/avm/res/network/express-route-circuit/main.bicep +++ b/avm/res/network/express-route-circuit/main.bicep @@ -60,6 +60,9 @@ param location string = resourceGroup().location @description('Optional. Allow classic operations. You can connect to virtual networks in the classic deployment model by setting allowClassicOperations to true.') param allowClassicOperations bool = false +@description('Optional. List of names for ExpressRoute circuit authorizations to create. To fetch the `authorizationKey` for the authorization, use the `existing` resource reference for `Microsoft.Network/expressRouteCircuits/authorizations`.') +param authorizationNames string[] = [] + @description('Optional. The bandwidth of the circuit when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. Default value of 0 will set the property to null.') param bandwidthInGbps int = 0 @@ -143,6 +146,11 @@ resource expressRouteCircuit 'Microsoft.Network/expressRouteCircuits@2023-04-01' } properties: { allowClassicOperations: allowClassicOperations + authorizations: [ + for authorizationName in authorizationNames: { + name: authorizationName + } + ] globalReachEnabled: globalReachEnabled bandwidthInGbps: bandwidthInGbps != 0 ? bandwidthInGbps : null expressRoutePort: !empty(expressRoutePortResourceId) @@ -245,6 +253,9 @@ output name string = expressRouteCircuit.name @description('The service key of the express route circuit.') output serviceKey string = expressRouteCircuit.properties.serviceKey +@description('The service provider provisioning state of the express route circuit.') +output serviceProviderProvisioningState string = expressRouteCircuit.properties.serviceProviderProvisioningState + @description('The location the resource was deployed into.') output location string = expressRouteCircuit.location diff --git a/avm/res/network/express-route-circuit/main.json b/avm/res/network/express-route-circuit/main.json index db47e4afee..9029bd322a 100644 --- a/avm/res/network/express-route-circuit/main.json +++ b/avm/res/network/express-route-circuit/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17484476736436996858" + "version": "0.33.93.31351", + "templateHash": "8776591928362518786" }, "name": "ExpressRoute Circuits", "description": "This module deploys an Express Route Circuit." @@ -346,6 +346,16 @@ "description": "Optional. Allow classic operations. You can connect to virtual networks in the classic deployment model by setting allowClassicOperations to true." } }, + "authorizationNames": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. List of names for ExpressRoute circuit authorizations to create. To fetch the `authorizationKey` for the authorization, use the `existing` resource reference for `Microsoft.Network/expressRouteCircuits/authorizations`." + } + }, "bandwidthInGbps": { "type": "int", "defaultValue": 0, @@ -450,6 +460,15 @@ "family": "[if(equals(parameters('skuTier'), 'Local'), 'UnlimitedData', parameters('skuFamily'))]" }, "properties": { + "copy": [ + { + "name": "authorizations", + "count": "[length(parameters('authorizationNames'))]", + "input": { + "name": "[parameters('authorizationNames')[copyIndex('authorizations')]]" + } + } + ], "allowClassicOperations": "[parameters('allowClassicOperations')]", "globalReachEnabled": "[parameters('globalReachEnabled')]", "bandwidthInGbps": "[if(not(equals(parameters('bandwidthInGbps'), 0)), parameters('bandwidthInGbps'), null())]", @@ -569,6 +588,13 @@ }, "value": "[reference('expressRouteCircuit').serviceKey]" }, + "serviceProviderProvisioningState": { + "type": "string", + "metadata": { + "description": "The service provider provisioning state of the express route circuit." + }, + "value": "[reference('expressRouteCircuit').serviceProviderProvisioningState]" + }, "location": { "type": "string", "metadata": { diff --git a/avm/res/network/express-route-circuit/version.json b/avm/res/network/express-route-circuit/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/express-route-circuit/version.json +++ b/avm/res/network/express-route-circuit/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] From 9de7b6f37dad379527258ba6c3f34ae9951f5bc1 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 11 Feb 2025 08:32:25 +0100 Subject: [PATCH 13/53] feat: RSV - Addressed diverse tickets & refactored structure (#4361) ## Description > Rebased from #3821 - Updated CMK implementation to align with AVM & the [docs](https://learn.microsoft.com/en-us/azure/backup/encryption-at-rest-with-cmk?tabs=portal) - Added UDTs for all objects & arrays (exept one due to its complexity) - Updaetd protectedItem implementation & added test case - Regenerated all files - Tested different combinations of configurations: - `enableAutokeyRotation` disabled ![image](https://github.com/user-attachments/assets/c156987a-7a17-413c-a6e3-28dbda8092e3) - `enableAutokeyRotation` enabled ![image](https://github.com/user-attachments/assets/0c427cce-89f8-405d-8eeb-8934c0d05ef7) Closes #3746 Closes #3625 Closes #3591 Closes #3233 Closes #2541 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.recovery-services.vault](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2Falexanderojala_rsv_rebase&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Ojala --- avm/res/recovery-services/vault/README.md | 1189 ++++++++++++-- .../vault/backup-config/main.json | 4 +- .../protection-container/README.md | 135 -- .../protection-container/main.bicep | 82 - .../protection-container/main.json | 275 ---- .../protected-item/README.md | 8 +- .../protected-item/main.bicep | 16 +- .../protected-item/main.json | 12 +- .../vault/backup-policy/README.md | 2 +- .../vault/backup-policy/main.bicep | 2 +- .../vault/backup-policy/main.json | 6 +- .../vault/backup-storage-config/main.json | 4 +- avm/res/recovery-services/vault/main.bicep | 373 ++++- avm/res/recovery-services/vault/main.json | 1439 +++++++++++++---- .../vault/replication-alert-setting/README.md | 5 +- .../replication-alert-setting/main.bicep | 6 +- .../vault/replication-alert-setting/main.json | 26 +- .../vault/replication-fabric/README.md | 80 +- .../vault/replication-fabric/main.bicep | 28 +- .../vault/replication-fabric/main.json | 282 +++- .../README.md | 58 +- .../main.bicep | 50 +- .../main.json | 164 +- .../README.md | 25 +- .../main.bicep | 39 +- .../main.json | 55 +- .../vault/replication-policy/main.json | 4 +- .../vault/tests/e2e/dr/main.test.bicep | 8 +- .../vault/tests/e2e/encr/dependencies.bicep | 61 + .../vault/tests/e2e/encr/main.test.bicep | 73 + .../vault/tests/e2e/max/dependencies.bicep | 110 +- .../vault/tests/e2e/max/main.test.bicep | 19 +- .../tests/e2e/waf-aligned/main.test.bicep | 7 +- avm/res/recovery-services/vault/version.json | 2 +- 34 files changed, 3395 insertions(+), 1254 deletions(-) delete mode 100644 avm/res/recovery-services/vault/backup-fabric/protection-container/README.md delete mode 100644 avm/res/recovery-services/vault/backup-fabric/protection-container/main.bicep delete mode 100644 avm/res/recovery-services/vault/backup-fabric/protection-container/main.json create mode 100644 avm/res/recovery-services/vault/tests/e2e/encr/dependencies.bicep create mode 100644 avm/res/recovery-services/vault/tests/e2e/encr/main.test.bicep diff --git a/avm/res/recovery-services/vault/README.md b/avm/res/recovery-services/vault/README.md index bb0a07fd8f..959ce67a48 100644 --- a/avm/res/recovery-services/vault/README.md +++ b/avm/res/recovery-services/vault/README.md @@ -20,11 +20,10 @@ This module deploys a Recovery Services Vault. | `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-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) | -| `Microsoft.RecoveryServices/vaults` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults) | +| `Microsoft.RecoveryServices/vaults` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2024-04-01/vaults) | | `Microsoft.RecoveryServices/vaults/backupconfig` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupconfig) | -| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers) | -| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | -| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupPolicies) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2024-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2024-10-01/vaults/backupFabrics/protectionContainers/protectedItems) | +| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2024-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2024-10-01/vaults/backupPolicies) | | `Microsoft.RecoveryServices/vaults/backupstorageconfig` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupstorageconfig) | | `Microsoft.RecoveryServices/vaults/replicationAlertSettings` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-10-01/vaults/replicationAlertSettings) | | `Microsoft.RecoveryServices/vaults/replicationFabrics` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-10-01/vaults/replicationFabrics) | @@ -42,8 +41,9 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Test case for disaster recovery enabled](#example-2-test-case-for-disaster-recovery-enabled) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [WAF-aligned](#example-4-waf-aligned) +- [Using encryption with Customer-Managed-Key](#example-3-using-encryption-with-customer-managed-key) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -171,24 +171,24 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { location: 'NorthEurope' replicationContainers: [ { - name: 'ne-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerName: 'pluto' - targetProtectionContainerId: '' + targetProtectionContainerResourceId: '' } ] + name: 'ne-container1' } { - name: 'ne-container2' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'WE-2' targetContainerName: 'we-container1' } ] + name: 'ne-container2' } ] } @@ -197,14 +197,14 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { name: 'WE-2' replicationContainers: [ { - name: 'we-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'NorthEurope' targetContainerName: 'ne-container2' } ] + name: 'we-container1' } ] } @@ -256,24 +256,24 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { "location": "NorthEurope", "replicationContainers": [ { - "name": "ne-container1", - "replicationContainerMappings": [ + "mappings": [ { "policyName": "Default_values", "targetContainerName": "pluto", - "targetProtectionContainerId": "" + "targetProtectionContainerResourceId": "" } - ] + ], + "name": "ne-container1" }, { - "name": "ne-container2", - "replicationContainerMappings": [ + "mappings": [ { "policyName": "Default_values", "targetContainerFabricName": "WE-2", "targetContainerName": "we-container1" } - ] + ], + "name": "ne-container2" } ] }, @@ -282,14 +282,14 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { "name": "WE-2", "replicationContainers": [ { - "name": "we-container1", - "replicationContainerMappings": [ + "mappings": [ { "policyName": "Default_values", "targetContainerFabricName": "NorthEurope", "targetContainerName": "ne-container2" } - ] + ], + "name": "we-container1" } ] } @@ -339,24 +339,24 @@ param replicationFabrics = [ location: 'NorthEurope' replicationContainers: [ { - name: 'ne-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerName: 'pluto' - targetProtectionContainerId: '' + targetProtectionContainerResourceId: '' } ] + name: 'ne-container1' } { - name: 'ne-container2' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'WE-2' targetContainerName: 'we-container1' } ] + name: 'ne-container2' } ] } @@ -365,14 +365,14 @@ param replicationFabrics = [ name: 'WE-2' replicationContainers: [ { - name: 'we-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'NorthEurope' targetContainerName: 'ne-container2' } ] + name: 'we-container1' } ] } @@ -399,7 +399,108 @@ param tags = {

-### Example 3: _Using large parameter set_ +### Example 3: _Using encryption with Customer-Managed-Key_ + +This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. + + +

+ +via Bicep module + +```bicep +module vault 'br/public:avm/res/recovery-services/vault:' = { + name: 'vaultDeployment' + params: { + // Required parameters + name: 'rsvencr001' + // Non-required parameters + customerManagedKey: { + autoRotationEnabled: false + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } + location: '' + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "rsvencr001" + }, + // Non-required parameters + "customerManagedKey": { + "value": { + "autoRotationEnabled": false, + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, + "location": { + "value": "" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/recovery-services/vault:' + +// Required parameters +param name = 'rsvencr001' +// Non-required parameters +param customerManagedKey = { + autoRotationEnabled: false + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' +} +param location = '' +param managedIdentities = { + userAssignedResourceIds: [ + '' + ] +} +``` + +
+

+ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -649,10 +750,6 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ] - backupStorageConfig: { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' - } diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -680,10 +777,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } monitoringSettings: { azureMonitorAlertSettings: { + alertsForAllFailoverIssues: 'Enabled' alertsForAllJobFailures: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } privateEndpoints: [ @@ -745,6 +845,15 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ] + protectedItems: [ + { + name: '' + policyName: 'VMpolicy' + protectedItemType: 'Microsoft.Compute/virtualMachines' + protectionContainerName: '' + sourceResourceId: '' + } + ] replicationAlertSettings: { customEmailAddresses: [ 'test.user@testcompany.com' @@ -1040,12 +1149,6 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } ] }, - "backupStorageConfig": { - "value": { - "crossRegionRestoreFlag": true, - "storageModelType": "GeoRedundant" - } - }, "diagnosticSettings": { "value": [ { @@ -1082,10 +1185,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { "monitoringSettings": { "value": { "azureMonitorAlertSettings": { - "alertsForAllJobFailures": "Enabled" + "alertsForAllFailoverIssues": "Enabled", + "alertsForAllJobFailures": "Enabled", + "alertsForAllReplicationIssues": "Enabled" }, "classicAlertSettings": { - "alertsForCriticalOperations": "Enabled" + "alertsForCriticalOperations": "Enabled", + "emailNotificationsForSiteRecovery": "Enabled" } } }, @@ -1150,6 +1256,17 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } ] }, + "protectedItems": { + "value": [ + { + "name": "", + "policyName": "VMpolicy", + "protectedItemType": "Microsoft.Compute/virtualMachines", + "protectionContainerName": "", + "sourceResourceId": "" + } + ] + }, "replicationAlertSettings": { "value": { "customEmailAddresses": [ @@ -1445,10 +1562,6 @@ param backupPolicies = [ } } ] -param backupStorageConfig = { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' -} param diagnosticSettings = [ { eventHubAuthorizationRuleResourceId: '' @@ -1476,10 +1589,13 @@ param managedIdentities = { } param monitoringSettings = { azureMonitorAlertSettings: { + alertsForAllFailoverIssues: 'Enabled' alertsForAllJobFailures: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } param privateEndpoints = [ @@ -1541,6 +1657,15 @@ param privateEndpoints = [ } } ] +param protectedItems = [ + { + name: '' + policyName: 'VMpolicy' + protectedItemType: 'Microsoft.Compute/virtualMachines' + protectionContainerName: '' + sourceResourceId: '' + } +] param replicationAlertSettings = { customEmailAddresses: [ 'test.user@testcompany.com' @@ -1582,7 +1707,7 @@ param tags = {

-### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -1832,10 +1957,6 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ] - backupStorageConfig: { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' - } diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -1863,10 +1984,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } monitoringSettings: { azureMonitorAlertSettings: { + alertsForAllFailoverIssues: 'Enabled' alertsForAllJobFailures: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } privateEndpoints: [ @@ -2204,12 +2328,6 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } ] }, - "backupStorageConfig": { - "value": { - "crossRegionRestoreFlag": true, - "storageModelType": "GeoRedundant" - } - }, "diagnosticSettings": { "value": [ { @@ -2246,10 +2364,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { "monitoringSettings": { "value": { "azureMonitorAlertSettings": { - "alertsForAllJobFailures": "Enabled" + "alertsForAllFailoverIssues": "Enabled", + "alertsForAllJobFailures": "Enabled", + "alertsForAllReplicationIssues": "Enabled" }, "classicAlertSettings": { - "alertsForCriticalOperations": "Enabled" + "alertsForCriticalOperations": "Enabled", + "emailNotificationsForSiteRecovery": "Enabled" } } }, @@ -2588,10 +2709,6 @@ param backupPolicies = [ } } ] -param backupStorageConfig = { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' -} param diagnosticSettings = [ { eventHubAuthorizationRuleResourceId: '' @@ -2619,10 +2736,13 @@ param managedIdentities = { } param monitoringSettings = { azureMonitorAlertSettings: { + alertsForAllFailoverIssues: 'Enabled' alertsForAllJobFailures: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } param privateEndpoints = [ @@ -2721,6 +2841,7 @@ param tags = { | [`backupConfig`](#parameter-backupconfig) | object | The backup configuration. | | [`backupPolicies`](#parameter-backuppolicies) | array | List of all backup policies. | | [`backupStorageConfig`](#parameter-backupstorageconfig) | object | The storage configuration for the Azure Recovery Service Vault. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`location`](#parameter-location) | string | Location for all resources. | @@ -2728,11 +2849,13 @@ param tags = { | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | | [`monitoringSettings`](#parameter-monitoringsettings) | object | Monitoring Settings of the vault. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | -| [`protectionContainers`](#parameter-protectioncontainers) | array | List of all protection containers. | +| [`protectedItems`](#parameter-protecteditems) | array | List of all protection containers. | | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. | +| [`redundancySettings`](#parameter-redundancysettings) | object | The redundancy settings of the vault. | | [`replicationAlertSettings`](#parameter-replicationalertsettings) | object | Replication alert settings. | | [`replicationFabrics`](#parameter-replicationfabrics) | array | List of all replication fabrics. | | [`replicationPolicies`](#parameter-replicationpolicies) | array | List of all replication policies. | +| [`restoreSettings`](#parameter-restoresettings) | object | The restore settings of the vault. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`securitySettings`](#parameter-securitysettings) | object | Security Settings of the vault. | | [`tags`](#parameter-tags) | object | Tags of the Recovery Service Vault resource. | @@ -2750,166 +2873,387 @@ The backup configuration. - Required: No - Type: object -- Default: `{}` -### Parameter: `backupPolicies` +**Optional parameters** -List of all backup policies. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enhancedSecurityState`](#parameter-backupconfigenhancedsecuritystate) | string | Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations. | +| [`isSoftDeleteFeatureStateEditable`](#parameter-backupconfigissoftdeletefeaturestateeditable) | bool | Is soft delete feature state editable. | +| [`name`](#parameter-backupconfigname) | string | Name of the Azure Recovery Service Vault Backup Policy. | +| [`resourceGuardOperationRequests`](#parameter-backupconfigresourceguardoperationrequests) | array | ResourceGuard Operation Requests. | +| [`softDeleteFeatureState`](#parameter-backupconfigsoftdeletefeaturestate) | string | Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes. | +| [`storageModelType`](#parameter-backupconfigstoragemodeltype) | string | Storage type. | +| [`storageType`](#parameter-backupconfigstoragetype) | string | Storage type. | +| [`storageTypeState`](#parameter-backupconfigstoragetypestate) | string | Once a machine is registered against a resource, the storageTypeState is always Locked. | + +### Parameter: `backupConfig.enhancedSecurityState` + +Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations. - Required: No -- Type: array -- Default: `[]` +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -### Parameter: `backupStorageConfig` +### Parameter: `backupConfig.isSoftDeleteFeatureStateEditable` -The storage configuration for the Azure Recovery Service Vault. +Is soft delete feature state editable. - Required: No -- Type: object -- Default: `{}` +- Type: bool -### Parameter: `diagnosticSettings` +### Parameter: `backupConfig.name` -The diagnostic settings of the service. +Name of the Azure Recovery Service Vault Backup Policy. + +- Required: No +- Type: string + +### Parameter: `backupConfig.resourceGuardOperationRequests` + +ResourceGuard Operation Requests. - Required: No - Type: array -**Optional parameters** +### Parameter: `backupConfig.softDeleteFeatureState` -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | -| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | -| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | -| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | -| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | -| [`name`](#parameter-diagnosticsettingsname) | string | The name of the diagnostic setting. | -| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | -| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | +Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes. -### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -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. +### Parameter: `backupConfig.storageModelType` + +Storage type. - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' + ] + ``` -### Parameter: `diagnosticSettings.eventHubName` +### Parameter: `backupConfig.storageType` -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. +Storage type. - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' + ] + ``` -### Parameter: `diagnosticSettings.logAnalyticsDestinationType` +### Parameter: `backupConfig.storageTypeState` -A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. +Once a machine is registered against a resource, the storageTypeState is always Locked. - Required: No - Type: string - Allowed: ```Bicep [ - 'AzureDiagnostics' - 'Dedicated' + 'Locked' + 'Unlocked' ] ``` -### Parameter: `diagnosticSettings.logCategoriesAndGroups` +### Parameter: `backupPolicies` -The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. +List of all backup policies. - Required: No - Type: array -**Optional parameters** +**Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | -| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | -| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | +| [`name`](#parameter-backuppoliciesname) | string | Name of the Azure Recovery Service Vault Backup Policy. | +| [`properties`](#parameter-backuppoliciesproperties) | object | Configuration of the Azure Recovery Service Vault Backup Policy. | -### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` +### Parameter: `backupPolicies.name` -Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. +Name of the Azure Recovery Service Vault Backup Policy. -- Required: No +- Required: Yes - Type: string -### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` +### Parameter: `backupPolicies.properties` -Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. +Configuration of the Azure Recovery Service Vault Backup Policy. + +- Required: Yes +- Type: object + +### Parameter: `backupStorageConfig` + +The storage configuration for the Azure Recovery Service Vault. - Required: No -- Type: string +- Type: object -### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` +**Optional parameters** -Enable or disable the category explicitly. Default is `true`. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`crossRegionRestoreFlag`](#parameter-backupstorageconfigcrossregionrestoreflag) | bool | Opt in details of Cross Region Restore feature. | +| [`name`](#parameter-backupstorageconfigname) | string | The name of the backup storage config. | +| [`storageModelType`](#parameter-backupstorageconfigstoragemodeltype) | string | Change Vault Storage Type (Works if vault has not registered any backup instance). | + +### Parameter: `backupStorageConfig.crossRegionRestoreFlag` + +Opt in details of Cross Region Restore feature. - Required: No - Type: bool -### Parameter: `diagnosticSettings.marketplacePartnerResourceId` +### Parameter: `backupStorageConfig.name` -The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. +The name of the backup storage config. - Required: No - Type: string -### Parameter: `diagnosticSettings.metricCategories` +### Parameter: `backupStorageConfig.storageModelType` -The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. +Change Vault Storage Type (Works if vault has not registered any backup instance). - Required: No -- Type: array +- Type: string +- Allowed: + ```Bicep + [ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' + ] + ``` + +### Parameter: `customerManagedKey` + +The customer managed key definition. + +- Required: No +- Type: object **Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | +| [`keyName`](#parameter-customermanagedkeykeyname) | string | The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | +| [`autoRotationEnabled`](#parameter-customermanagedkeyautorotationenabled) | bool | Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | -### Parameter: `diagnosticSettings.metricCategories.category` +### Parameter: `customerManagedKey.keyName` -Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. +The name of the customer managed key to use for encryption. - Required: Yes - Type: string -### Parameter: `diagnosticSettings.metricCategories.enabled` +### Parameter: `customerManagedKey.keyVaultResourceId` -Enable or disable the category explicitly. Default is `true`. +The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.autoRotationEnabled` + +Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used. - Required: No - Type: bool -### Parameter: `diagnosticSettings.name` +### Parameter: `customerManagedKey.keyVersion` -The name of the diagnostic setting. +The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting. - Required: No - Type: string -### Parameter: `diagnosticSettings.storageAccountResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` -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. +User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. - Required: No - Type: string -### Parameter: `diagnosticSettings.workspaceResourceId` +### Parameter: `diagnosticSettings` -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. +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of the diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of the diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +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. - Required: No - Type: string @@ -3000,7 +3344,112 @@ Monitoring Settings of the vault. - Required: No - Type: object -- Default: `{}` + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`azureMonitorAlertSettings`](#parameter-monitoringsettingsazuremonitoralertsettings) | object | The alert settings. | +| [`classicAlertSettings`](#parameter-monitoringsettingsclassicalertsettings) | object | The classic alert settings. | + +### Parameter: `monitoringSettings.azureMonitorAlertSettings` + +The alert settings. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`alertsForAllFailoverIssues`](#parameter-monitoringsettingsazuremonitoralertsettingsalertsforallfailoverissues) | string | Enable / disable alerts for all failover issues. | +| [`alertsForAllJobFailures`](#parameter-monitoringsettingsazuremonitoralertsettingsalertsforalljobfailures) | string | Enable / disable alerts for all job failures. | +| [`alertsForAllReplicationIssues`](#parameter-monitoringsettingsazuremonitoralertsettingsalertsforallreplicationissues) | string | Enable / disable alerts for all replication issues. | + +### Parameter: `monitoringSettings.azureMonitorAlertSettings.alertsForAllFailoverIssues` + +Enable / disable alerts for all failover issues. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `monitoringSettings.azureMonitorAlertSettings.alertsForAllJobFailures` + +Enable / disable alerts for all job failures. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `monitoringSettings.azureMonitorAlertSettings.alertsForAllReplicationIssues` + +Enable / disable alerts for all replication issues. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `monitoringSettings.classicAlertSettings` + +The classic alert settings. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`alertsForCriticalOperations`](#parameter-monitoringsettingsclassicalertsettingsalertsforcriticaloperations) | string | Enable / disable alerts for critical operations. | +| [`emailNotificationsForSiteRecovery`](#parameter-monitoringsettingsclassicalertsettingsemailnotificationsforsiterecovery) | string | Enable / disable email notifications for site recovery. | + +### Parameter: `monitoringSettings.classicAlertSettings.alertsForCriticalOperations` + +Enable / disable alerts for critical operations. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `monitoringSettings.classicAlertSettings.emailNotificationsForSiteRecovery` + +Enable / disable email notifications for site recovery. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` ### Parameter: `privateEndpoints` @@ -3413,52 +3862,391 @@ Tags to be applied on all resources/Resource Groups in this deployment. - Required: No - Type: object -### Parameter: `protectionContainers` +### Parameter: `protectedItems` List of all protection containers. - Required: No - Type: array -- Default: `[]` -### Parameter: `publicNetworkAccess` +**Required parameters** -Whether or not public network access is allowed for this resource. For security reasons it should be disabled. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-protecteditemsname) | string | Name of the resource. | +| [`policyName`](#parameter-protecteditemspolicyname) | string | The backup policy with which this item is backed up. | +| [`protectedItemType`](#parameter-protecteditemsprotecteditemtype) | string | The backup item type. | +| [`protectionContainerName`](#parameter-protecteditemsprotectioncontainername) | string | Name of the Azure Recovery Service Vault Protection Container. | +| [`sourceResourceId`](#parameter-protecteditemssourceresourceid) | string | Resource ID of the resource to back up. | -- Required: No -- Type: string -- Default: `'Disabled'` -- Allowed: - ```Bicep - [ - 'Disabled' - 'Enabled' - ] - ``` +**Optional parameters** -### Parameter: `replicationAlertSettings` +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-protecteditemslocation) | string | Location for all resources. | -Replication alert settings. +### Parameter: `protectedItems.name` -- Required: No -- Type: object -- Default: `{}` +Name of the resource. -### Parameter: `replicationFabrics` +- Required: Yes +- Type: string -List of all replication fabrics. +### Parameter: `protectedItems.policyName` -- Required: No -- Type: array -- Default: `[]` +The backup policy with which this item is backed up. -### Parameter: `replicationPolicies` +- Required: Yes +- Type: string -List of all replication policies. +### Parameter: `protectedItems.protectedItemType` + +The backup item type. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AzureFileShareProtectedItem' + 'AzureVmWorkloadSAPAseDatabase' + 'AzureVmWorkloadSAPHanaDatabase' + 'AzureVmWorkloadSQLDatabase' + 'DPMProtectedItem' + 'GenericProtectedItem' + 'MabFileFolderProtectedItem' + 'Microsoft.ClassicCompute/virtualMachines' + 'Microsoft.Compute/virtualMachines' + 'Microsoft.Sql/servers/databases' + ] + ``` + +### Parameter: `protectedItems.protectionContainerName` + +Name of the Azure Recovery Service Vault Protection Container. + +- Required: Yes +- Type: string + +### Parameter: `protectedItems.sourceResourceId` + +Resource ID of the resource to back up. + +- Required: Yes +- Type: string + +### Parameter: `protectedItems.location` + +Location for all resources. + +- Required: No +- Type: string + +### Parameter: `publicNetworkAccess` + +Whether or not public network access is allowed for this resource. For security reasons it should be disabled. + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `redundancySettings` + +The redundancy settings of the vault. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`crossRegionRestore`](#parameter-redundancysettingscrossregionrestore) | string | Flag to show if Cross Region Restore is enabled on the Vault or not. | +| [`standardTierStorageRedundancy`](#parameter-redundancysettingsstandardtierstorageredundancy) | string | The storage redundancy setting of a vault. | + +### Parameter: `redundancySettings.crossRegionRestore` + +Flag to show if Cross Region Restore is enabled on the Vault or not. + +- Required: No +- Type: string + +### Parameter: `redundancySettings.standardTierStorageRedundancy` + +The storage redundancy setting of a vault. + +- Required: No +- Type: string + +### Parameter: `replicationAlertSettings` + +Replication alert settings. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`customEmailAddresses`](#parameter-replicationalertsettingscustomemailaddresses) | array | The custom email address for sending emails. | +| [`locale`](#parameter-replicationalertsettingslocale) | string | The locale for the email notification. | +| [`name`](#parameter-replicationalertsettingsname) | string | The name of the replication Alert Setting. | +| [`sendToOwners`](#parameter-replicationalertsettingssendtoowners) | string | The value indicating whether to send email to subscription administrator. | + +### Parameter: `replicationAlertSettings.customEmailAddresses` + +The custom email address for sending emails. - Required: No - Type: array -- Default: `[]` + +### Parameter: `replicationAlertSettings.locale` + +The locale for the email notification. + +- Required: No +- Type: string + +### Parameter: `replicationAlertSettings.name` + +The name of the replication Alert Setting. + +- Required: No +- Type: string + +### Parameter: `replicationAlertSettings.sendToOwners` + +The value indicating whether to send email to subscription administrator. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'DoNotSend' + 'Send' + ] + ``` + +### Parameter: `replicationFabrics` + +List of all replication fabrics. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-replicationfabricslocation) | string | The recovery location the fabric represents. | +| [`name`](#parameter-replicationfabricsname) | string | The name of the fabric. | +| [`replicationContainers`](#parameter-replicationfabricsreplicationcontainers) | array | Replication containers to create. | + +### Parameter: `replicationFabrics.location` + +The recovery location the fabric represents. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.name` + +The name of the fabric. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers` + +Replication containers to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-replicationfabricsreplicationcontainersname) | string | The name of the replication container. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mappings`](#parameter-replicationfabricsreplicationcontainersmappings) | array | Replication containers mappings to create. | + +### Parameter: `replicationFabrics.replicationContainers.name` + +The name of the replication container. + +- Required: Yes +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings` + +Replication containers mappings to create. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-replicationfabricsreplicationcontainersmappingsname) | string | The name of the replication container mapping. If not provided, it will be automatically generated as `-`. | +| [`policyName`](#parameter-replicationfabricsreplicationcontainersmappingspolicyname) | string | Name of the replication policy. Will be ignored if policyResourceId is also specified. | +| [`policyResourceId`](#parameter-replicationfabricsreplicationcontainersmappingspolicyresourceid) | string | Resource ID of the replication policy. If defined, policyName will be ignored. | +| [`targetContainerFabricName`](#parameter-replicationfabricsreplicationcontainersmappingstargetcontainerfabricname) | string | Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetContainerName`](#parameter-replicationfabricsreplicationcontainersmappingstargetcontainername) | string | Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetProtectionContainerResourceId`](#parameter-replicationfabricsreplicationcontainersmappingstargetprotectioncontainerresourceid) | string | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | + +### Parameter: `replicationFabrics.replicationContainers.mappings.name` + +The name of the replication container mapping. If not provided, it will be automatically generated as `-`. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings.policyName` + +Name of the replication policy. Will be ignored if policyResourceId is also specified. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings.policyResourceId` + +Resource ID of the replication policy. If defined, policyName will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings.targetContainerFabricName` + +Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings.targetContainerName` + +Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationFabrics.replicationContainers.mappings.targetProtectionContainerResourceId` + +Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationPolicies` + +List of all replication policies. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-replicationpoliciesname) | string | The name of the replication policy. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`appConsistentFrequencyInMinutes`](#parameter-replicationpoliciesappconsistentfrequencyinminutes) | int | The app consistent snapshot frequency (in minutes). | +| [`crashConsistentFrequencyInMinutes`](#parameter-replicationpoliciescrashconsistentfrequencyinminutes) | int | The crash consistent snapshot frequency (in minutes). | +| [`multiVmSyncStatus`](#parameter-replicationpoliciesmultivmsyncstatus) | string | A value indicating whether multi-VM sync has to be enabled. | +| [`recoveryPointHistory`](#parameter-replicationpoliciesrecoverypointhistory) | int | The duration in minutes until which the recovery points need to be stored. | + +### Parameter: `replicationPolicies.name` + +The name of the replication policy. + +- Required: Yes +- Type: string + +### Parameter: `replicationPolicies.appConsistentFrequencyInMinutes` + +The app consistent snapshot frequency (in minutes). + +- Required: No +- Type: int + +### Parameter: `replicationPolicies.crashConsistentFrequencyInMinutes` + +The crash consistent snapshot frequency (in minutes). + +- Required: No +- Type: int + +### Parameter: `replicationPolicies.multiVmSyncStatus` + +A value indicating whether multi-VM sync has to be enabled. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disable' + 'Enable' + ] + ``` + +### Parameter: `replicationPolicies.recoveryPointHistory` + +The duration in minutes until which the recovery points need to be stored. + +- Required: No +- Type: int + +### Parameter: `restoreSettings` + +The restore settings of the vault. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`crossSubscriptionRestoreSettings`](#parameter-restoresettingscrosssubscriptionrestoresettings) | object | The restore settings of the vault. | + +### Parameter: `restoreSettings.crossSubscriptionRestoreSettings` + +The restore settings of the vault. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`crossSubscriptionRestoreState`](#parameter-restoresettingscrosssubscriptionrestoresettingscrosssubscriptionrestorestate) | string | The restore settings of the vault. | + +### Parameter: `restoreSettings.crossSubscriptionRestoreSettings.crossSubscriptionRestoreState` + +The restore settings of the vault. + +- Required: Yes +- Type: string ### Parameter: `roleAssignments` @@ -3575,7 +4363,95 @@ Security Settings of the vault. - Required: No - Type: object -- Default: `{}` + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`immutabilitySettings`](#parameter-securitysettingsimmutabilitysettings) | object | Immutability settings of a vault. | +| [`softDeleteSettings`](#parameter-securitysettingssoftdeletesettings) | object | Soft delete settings of a vault. | + +### Parameter: `securitySettings.immutabilitySettings` + +Immutability settings of a vault. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`state`](#parameter-securitysettingsimmutabilitysettingsstate) | string | The immmutability setting of the vault. | + +### Parameter: `securitySettings.immutabilitySettings.state` + +The immmutability setting of the vault. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Locked' + 'Unlocked' + ] + ``` + +### Parameter: `securitySettings.softDeleteSettings` + +Soft delete settings of a vault. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enhancedSecurityState`](#parameter-securitysettingssoftdeletesettingsenhancedsecuritystate) | string | The enhanced security state. | +| [`softDeleteRetentionPeriodInDays`](#parameter-securitysettingssoftdeletesettingssoftdeleteretentionperiodindays) | int | The soft delete retention period in days. | +| [`softDeleteState`](#parameter-securitysettingssoftdeletesettingssoftdeletestate) | string | The soft delete state. | + +### Parameter: `securitySettings.softDeleteSettings.enhancedSecurityState` + +The enhanced security state. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AlwaysON' + 'Disabled' + 'Enabled' + 'Invalid' + ] + ``` + +### Parameter: `securitySettings.softDeleteSettings.softDeleteRetentionPeriodInDays` + +The soft delete retention period in days. + +- Required: Yes +- Type: int + +### Parameter: `securitySettings.softDeleteSettings.softDeleteState` + +The soft delete state. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AlwaysON' + 'Disabled' + 'Enabled' + 'Invalid' + ] + ``` ### Parameter: `tags` @@ -3602,6 +4478,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | | `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.4.0` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/recovery-services/vault/backup-config/main.json b/avm/res/recovery-services/vault/backup-config/main.json index 282e69a1d2..b855197f7c 100644 --- a/avm/res/recovery-services/vault/backup-config/main.json +++ b/avm/res/recovery-services/vault/backup-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "617152935951311267" + "version": "0.33.13.18514", + "templateHash": "1001038691929568740" }, "name": "Recovery Services Vault Backup Config", "description": "This module deploys a Recovery Services Vault Backup Config." diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/README.md b/avm/res/recovery-services/vault/backup-fabric/protection-container/README.md deleted file mode 100644 index 23baa4b6f9..0000000000 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# Recovery Services Vault Protection Container `[Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers]` - -This module deploys a Recovery Services Vault Protection Container. - -## Navigation - -- [Resource Types](#Resource-Types) -- [Parameters](#Parameters) -- [Outputs](#Outputs) - -## Resource Types - -| Resource Type | API Version | -| :-- | :-- | -| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers) | -| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | - -## Parameters - -**Required parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`name`](#parameter-name) | string | Name of the Azure Recovery Service Vault Protection Container. | - -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`recoveryVaultName`](#parameter-recoveryvaultname) | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | - -**Optional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`backupManagementType`](#parameter-backupmanagementtype) | string | Backup management type to execute the current Protection Container job. | -| [`containerType`](#parameter-containertype) | string | Type of the container. | -| [`friendlyName`](#parameter-friendlyname) | string | Friendly name of the Protection Container. | -| [`location`](#parameter-location) | string | Location for all resources. | -| [`protectedItems`](#parameter-protecteditems) | array | Protected items to register in the container. | -| [`sourceResourceId`](#parameter-sourceresourceid) | string | Resource ID of the target resource for the Protection Container. | - -### Parameter: `name` - -Name of the Azure Recovery Service Vault Protection Container. - -- Required: Yes -- Type: string - -### Parameter: `recoveryVaultName` - -The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. - -- Required: Yes -- Type: string - -### Parameter: `backupManagementType` - -Backup management type to execute the current Protection Container job. - -- Required: No -- Type: string -- Allowed: - ```Bicep - [ - 'AzureBackupServer' - 'AzureIaasVM' - 'AzureSql' - 'AzureStorage' - 'AzureWorkload' - 'DefaultBackup' - 'DPM' - 'Invalid' - 'MAB' - ] - ``` - -### Parameter: `containerType` - -Type of the container. - -- Required: No -- Type: string -- Allowed: - ```Bicep - [ - 'AzureBackupServerContainer' - 'AzureSqlContainer' - 'GenericContainer' - 'Microsoft.ClassicCompute/virtualMachines' - 'Microsoft.Compute/virtualMachines' - 'SQLAGWorkLoadContainer' - 'StorageContainer' - 'VMAppContainer' - 'Windows' - ] - ``` - -### Parameter: `friendlyName` - -Friendly name of the Protection Container. - -- Required: No -- Type: string - -### Parameter: `location` - -Location for all resources. - -- Required: No -- Type: string -- Default: `[resourceGroup().location]` - -### Parameter: `protectedItems` - -Protected items to register in the container. - -- Required: No -- Type: array -- Default: `[]` - -### Parameter: `sourceResourceId` - -Resource ID of the target resource for the Protection Container. - -- Required: No -- Type: string - -## Outputs - -| Output | Type | Description | -| :-- | :-- | :-- | -| `name` | string | The Name of the Protection Container. | -| `resourceGroupName` | string | The name of the Resource Group the Protection Container was created in. | -| `resourceId` | string | The resource ID of the Protection Container. | diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.bicep b/avm/res/recovery-services/vault/backup-fabric/protection-container/main.bicep deleted file mode 100644 index 46f5bedc9a..0000000000 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.bicep +++ /dev/null @@ -1,82 +0,0 @@ -metadata name = 'Recovery Services Vault Protection Container' -metadata description = 'This module deploys a Recovery Services Vault Protection Container.' - -@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') -param recoveryVaultName string - -@description('Required. Name of the Azure Recovery Service Vault Protection Container.') -param name string - -@description('Optional. Location for all resources.') -param location string = resourceGroup().location - -@description('Optional. Backup management type to execute the current Protection Container job.') -@allowed([ - 'AzureBackupServer' - 'AzureIaasVM' - 'AzureSql' - 'AzureStorage' - 'AzureWorkload' - 'DPM' - 'DefaultBackup' - 'Invalid' - 'MAB' -]) -param backupManagementType string? - -@description('Optional. Resource ID of the target resource for the Protection Container.') -param sourceResourceId string? - -@description('Optional. Friendly name of the Protection Container.') -param friendlyName string? - -@description('Optional. Protected items to register in the container.') -param protectedItems array = [] - -@description('Optional. Type of the container.') -@allowed([ - 'AzureBackupServerContainer' - 'AzureSqlContainer' - 'GenericContainer' - 'Microsoft.ClassicCompute/virtualMachines' - 'Microsoft.Compute/virtualMachines' - 'SQLAGWorkLoadContainer' - 'StorageContainer' - 'VMAppContainer' - 'Windows' -]) -param containerType string? - -resource protectionContainer 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers@2023-01-01' = { - name: '${recoveryVaultName}/Azure/${name}' - properties: { - sourceResourceId: sourceResourceId - friendlyName: friendlyName - backupManagementType: backupManagementType - containerType: !empty(containerType) ? any(containerType) : null - } -} - -module protectionContainer_protectedItems 'protected-item/main.bicep' = [ - for (protectedItem, index) in protectedItems: { - name: '${uniqueString(deployment().name, location)}-ProtectedItem-${index}' - params: { - policyId: protectedItem.policyId - name: protectedItem.name - protectedItemType: protectedItem.protectedItemType - protectionContainerName: protectionContainer.name - recoveryVaultName: recoveryVaultName - sourceResourceId: protectedItem.sourceResourceId - location: location - } - } -] - -@description('The name of the Resource Group the Protection Container was created in.') -output resourceGroupName string = resourceGroup().name - -@description('The resource ID of the Protection Container.') -output resourceId string = protectionContainer.id - -@description('The Name of the Protection Container.') -output name string = protectionContainer.name diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json b/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json deleted file mode 100644 index 29fed457e4..0000000000 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "$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.32.4.45862", - "templateHash": "2342312580952650656" - }, - "name": "Recovery Services Vault Protection Container", - "description": "This module deploys a Recovery Services Vault Protection Container." - }, - "parameters": { - "recoveryVaultName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the Azure Recovery Service Vault Protection Container." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "backupManagementType": { - "type": "string", - "nullable": true, - "allowedValues": [ - "AzureBackupServer", - "AzureIaasVM", - "AzureSql", - "AzureStorage", - "AzureWorkload", - "DPM", - "DefaultBackup", - "Invalid", - "MAB" - ], - "metadata": { - "description": "Optional. Backup management type to execute the current Protection Container job." - } - }, - "sourceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the target resource for the Protection Container." - } - }, - "friendlyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Friendly name of the Protection Container." - } - }, - "protectedItems": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Protected items to register in the container." - } - }, - "containerType": { - "type": "string", - "nullable": true, - "allowedValues": [ - "AzureBackupServerContainer", - "AzureSqlContainer", - "GenericContainer", - "Microsoft.ClassicCompute/virtualMachines", - "Microsoft.Compute/virtualMachines", - "SQLAGWorkLoadContainer", - "StorageContainer", - "VMAppContainer", - "Windows" - ], - "metadata": { - "description": "Optional. Type of the container." - } - } - }, - "resources": { - "protectionContainer": { - "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers", - "apiVersion": "2023-01-01", - "name": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]", - "properties": { - "sourceResourceId": "[parameters('sourceResourceId')]", - "friendlyName": "[parameters('friendlyName')]", - "backupManagementType": "[parameters('backupManagementType')]", - "containerType": "[if(not(empty(parameters('containerType'))), parameters('containerType'), null())]" - } - }, - "protectionContainer_protectedItems": { - "copy": { - "name": "protectionContainer_protectedItems", - "count": "[length(parameters('protectedItems'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-ProtectedItem-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "policyId": { - "value": "[parameters('protectedItems')[copyIndex()].policyId]" - }, - "name": { - "value": "[parameters('protectedItems')[copyIndex()].name]" - }, - "protectedItemType": { - "value": "[parameters('protectedItems')[copyIndex()].protectedItemType]" - }, - "protectionContainerName": { - "value": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]" - }, - "recoveryVaultName": { - "value": "[parameters('recoveryVaultName')]" - }, - "sourceResourceId": { - "value": "[parameters('protectedItems')[copyIndex()].sourceResourceId]" - }, - "location": { - "value": "[parameters('location')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "10896311401307689711" - }, - "name": "Recovery Service Vaults Protection Container Protected Item", - "description": "This module deploys a Recovery Services Vault Protection Container Protected Item." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the resource." - } - }, - "protectionContainerName": { - "type": "string", - "metadata": { - "description": "Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment." - } - }, - "recoveryVaultName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "protectedItemType": { - "type": "string", - "allowedValues": [ - "AzureFileShareProtectedItem", - "AzureVmWorkloadSAPAseDatabase", - "AzureVmWorkloadSAPHanaDatabase", - "AzureVmWorkloadSQLDatabase", - "DPMProtectedItem", - "GenericProtectedItem", - "MabFileFolderProtectedItem", - "Microsoft.ClassicCompute/virtualMachines", - "Microsoft.Compute/virtualMachines", - "Microsoft.Sql/servers/databases" - ], - "metadata": { - "description": "Required. The backup item type." - } - }, - "policyId": { - "type": "string", - "metadata": { - "description": "Required. ID of the backup policy with which this item is backed up." - } - }, - "sourceResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the resource to back up." - } - } - }, - "resources": [ - { - "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems", - "apiVersion": "2023-01-01", - "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]", - "location": "[parameters('location')]", - "properties": { - "protectedItemType": "[parameters('protectedItemType')]", - "policyId": "[parameters('policyId')]", - "sourceResourceId": "[parameters('sourceResourceId')]" - } - } - ], - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Resource Group the protected item was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the protected item." - }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems', split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[2], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[3])]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The Name of the protected item." - }, - "value": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]" - } - } - } - }, - "dependsOn": [ - "protectionContainer" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Resource Group the Protection Container was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Protection Container." - }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers', split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[2])]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Protection Container." - }, - "value": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]" - } - } -} \ No newline at end of file diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/README.md b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/README.md index c66a0ad665..d0a07999d8 100644 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/README.md +++ b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/README.md @@ -12,7 +12,7 @@ This module deploys a Recovery Services Vault Protection Container Protected Ite | Resource Type | API Version | | :-- | :-- | -| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2024-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2024-10-01/vaults/backupFabrics/protectionContainers/protectedItems) | ## Parameters @@ -21,7 +21,7 @@ This module deploys a Recovery Services Vault Protection Container Protected Ite | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the resource. | -| [`policyId`](#parameter-policyid) | string | ID of the backup policy with which this item is backed up. | +| [`policyName`](#parameter-policyname) | string | The backup policy with which this item is backed up. | | [`protectedItemType`](#parameter-protecteditemtype) | string | The backup item type. | | [`sourceResourceId`](#parameter-sourceresourceid) | string | Resource ID of the resource to back up. | @@ -45,9 +45,9 @@ Name of the resource. - Required: Yes - Type: string -### Parameter: `policyId` +### Parameter: `policyName` -ID of the backup policy with which this item is backed up. +The backup policy with which this item is backed up. - Required: Yes - Type: string diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.bicep b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.bicep index 781a46df52..314684719e 100644 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.bicep +++ b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.bicep @@ -28,18 +28,26 @@ param location string = resourceGroup().location @description('Required. The backup item type.') param protectedItemType string -@description('Required. ID of the backup policy with which this item is backed up.') -param policyId string +@description('Required. The backup policy with which this item is backed up.') +param policyName string @description('Required. Resource ID of the resource to back up.') param sourceResourceId string -resource protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2023-01-01' = { +resource rsv 'Microsoft.RecoveryServices/vaults@2023-01-01' existing = { + name: recoveryVaultName + + resource backupPolicy 'backupPolicies@2024-10-01' existing = { + name: policyName + } +} + +resource protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2024-10-01' = { name: '${recoveryVaultName}/Azure/${protectionContainerName}/${name}' location: location properties: { protectedItemType: any(protectedItemType) - policyId: policyId + policyId: rsv::backupPolicy.id sourceResourceId: sourceResourceId } } diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json index 9bb32d1ceb..6abfc869ee 100644 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json +++ b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "10896311401307689711" + "version": "0.33.13.18514", + "templateHash": "14694277488209426813" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item." @@ -54,10 +54,10 @@ "description": "Required. The backup item type." } }, - "policyId": { + "policyName": { "type": "string", "metadata": { - "description": "Required. ID of the backup policy with which this item is backed up." + "description": "Required. The backup policy with which this item is backed up." } }, "sourceResourceId": { @@ -70,12 +70,12 @@ "resources": [ { "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems", - "apiVersion": "2023-01-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]", "location": "[parameters('location')]", "properties": { "protectedItemType": "[parameters('protectedItemType')]", - "policyId": "[parameters('policyId')]", + "policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryVaultName'), parameters('policyName'))]", "sourceResourceId": "[parameters('sourceResourceId')]" } } diff --git a/avm/res/recovery-services/vault/backup-policy/README.md b/avm/res/recovery-services/vault/backup-policy/README.md index 7a222bdeb3..b05afe092c 100644 --- a/avm/res/recovery-services/vault/backup-policy/README.md +++ b/avm/res/recovery-services/vault/backup-policy/README.md @@ -12,7 +12,7 @@ This module deploys a Recovery Services Vault Backup Policy. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupPolicies) | +| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2024-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2024-10-01/vaults/backupPolicies) | ## Parameters diff --git a/avm/res/recovery-services/vault/backup-policy/main.bicep b/avm/res/recovery-services/vault/backup-policy/main.bicep index cedc1bc33f..3e182f8f59 100644 --- a/avm/res/recovery-services/vault/backup-policy/main.bicep +++ b/avm/res/recovery-services/vault/backup-policy/main.bicep @@ -14,7 +14,7 @@ resource rsv 'Microsoft.RecoveryServices/vaults@2023-01-01' existing = { name: recoveryVaultName } -resource backupPolicy 'Microsoft.RecoveryServices/vaults/backupPolicies@2023-01-01' = { +resource backupPolicy 'Microsoft.RecoveryServices/vaults/backupPolicies@2024-10-01' = { name: name parent: rsv properties: properties diff --git a/avm/res/recovery-services/vault/backup-policy/main.json b/avm/res/recovery-services/vault/backup-policy/main.json index 45675065d7..aadb42246e 100644 --- a/avm/res/recovery-services/vault/backup-policy/main.json +++ b/avm/res/recovery-services/vault/backup-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8749531505758368980" + "version": "0.33.13.18514", + "templateHash": "18409788320626401161" }, "name": "Recovery Services Vault Backup Policies", "description": "This module deploys a Recovery Services Vault Backup Policy." @@ -33,7 +33,7 @@ "resources": [ { "type": "Microsoft.RecoveryServices/vaults/backupPolicies", - "apiVersion": "2023-01-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", "properties": "[parameters('properties')]" } diff --git a/avm/res/recovery-services/vault/backup-storage-config/main.json b/avm/res/recovery-services/vault/backup-storage-config/main.json index a1f71b911b..795ad5446f 100644 --- a/avm/res/recovery-services/vault/backup-storage-config/main.json +++ b/avm/res/recovery-services/vault/backup-storage-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5873261661461950688" + "version": "0.33.13.18514", + "templateHash": "17375948662410777678" }, "name": "Recovery Services Vault Backup Storage Config", "description": "This module deploys a Recovery Service Vault Backup Storage Configuration." diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 10cffcdaf9..96e98b1f01 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -5,7 +5,7 @@ metadata description = 'This module deploys a Recovery Services Vault.' param name string @description('Optional. The storage configuration for the Azure Recovery Service Vault.') -param backupStorageConfig object = {} +param backupStorageConfig backupStorageConfigType? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true @@ -14,22 +14,22 @@ param enableTelemetry bool = true param location string = resourceGroup().location @description('Optional. List of all backup policies.') -param backupPolicies array = [] +param backupPolicies backupPolicyType[]? @description('Optional. The backup configuration.') -param backupConfig object = {} +param backupConfig backupConfigType? @description('Optional. List of all protection containers.') -param protectionContainers array = [] +param protectedItems protectedItemType[]? @description('Optional. List of all replication fabrics.') -param replicationFabrics array = [] +param replicationFabrics replicationFabricType[]? @description('Optional. List of all replication policies.') -param replicationPolicies array = [] +param replicationPolicies replicationPolicyType[]? @description('Optional. Replication alert settings.') -param replicationAlertSettings object = {} +param replicationAlertSettings replicationAlertSettingsType? import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The diagnostic settings of the service.') @@ -55,10 +55,10 @@ import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-co param privateEndpoints privateEndpointSingleServiceType[]? @description('Optional. Monitoring Settings of the vault.') -param monitoringSettings object = {} +param monitoringSettings monitoringSettingsType? @description('Optional. Security Settings of the vault.') -param securitySettings object = {} +param securitySettings securitySettingType? @description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled.') @allowed([ @@ -67,6 +67,16 @@ param securitySettings object = {} ]) param publicNetworkAccess string = 'Disabled' +@description('Optional. The redundancy settings of the vault.') +param redundancySettings redundancySettingsType? + +@description('Optional. The restore settings of the vault.') +param restoreSettings restoreSettingsType? + +import { customerManagedKeyWithAutoRotateType } from 'br/public:avm/utl/types/avm-common-types:0.4.0' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyWithAutoRotateType? + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -150,7 +160,27 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource rsv 'Microsoft.RecoveryServices/vaults@2023-01-01' = { +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 rsv 'Microsoft.RecoveryServices/vaults@2024-04-01' = { name: name location: location tags: tags @@ -160,22 +190,57 @@ resource rsv 'Microsoft.RecoveryServices/vaults@2023-01-01' = { tier: 'Standard' } properties: { - monitoringSettings: !empty(monitoringSettings) ? monitoringSettings : null - securitySettings: !empty(securitySettings) ? securitySettings : null + monitoringSettings: !empty(monitoringSettings) + ? { + azureMonitorAlertSettings: !empty(monitoringSettings.?azureMonitorAlertSettings) + ? { + alertsForAllFailoverIssues: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllFailoverIssues ?? 'Enabled' + alertsForAllJobFailures: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllJobFailures ?? 'Enabled' + alertsForAllReplicationIssues: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllReplicationIssues ?? 'Enabled' + } + : null + classicAlertSettings: !empty(monitoringSettings.?classicAlertSettings) + ? { + alertsForCriticalOperations: monitoringSettings!.classicAlertSettings.?alertsForCriticalOperations ?? 'Enabled' + emailNotificationsForSiteRecovery: monitoringSettings!.classicAlertSettings.?emailNotificationsForSiteRecovery ?? 'Enabled' + } + : null + } + : null + securitySettings: securitySettings publicNetworkAccess: publicNetworkAccess + redundancySettings: redundancySettings + restoreSettings: restoreSettings + encryption: !empty(customerManagedKey) + ? { + infrastructureEncryption: 'Enabled' + kekIdentity: !empty(customerManagedKey.?userAssignedIdentityResourceId) + ? { + userAssignedIdentity: cMKUserAssignedIdentity.id + } + : { + useSystemAssignedIdentity: empty(customerManagedKey.?userAssignedIdentityResourceId) + } + keyVaultProperties: { + keyUri: !empty(customerManagedKey.?keyVersion) + ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' + : (customerManagedKey.?autoRotationEnabled ?? true) + ? cMKKeyVault::cMKKey.properties.keyUri + : cMKKeyVault::cMKKey.properties.keyUriWithVersion + } + } + : null } } module rsv_replicationFabrics 'replication-fabric/main.bicep' = [ - for (replicationFabric, index) in replicationFabrics: { + for (replicationFabric, index) in (replicationFabrics ?? []): { name: '${uniqueString(deployment().name, location)}-RSV-Fabric-${index}' params: { recoveryVaultName: rsv.name - name: contains(replicationFabric, 'name') ? replicationFabric.name : replicationFabric.location + name: replicationFabric.?name location: replicationFabric.location - replicationContainers: contains(replicationFabric, 'replicationContainers') - ? replicationFabric.replicationContainers - : [] + replicationContainers: replicationFabric.?replicationContainers } dependsOn: [ rsv_replicationPolicies @@ -184,23 +249,15 @@ module rsv_replicationFabrics 'replication-fabric/main.bicep' = [ ] module rsv_replicationPolicies 'replication-policy/main.bicep' = [ - for (replicationPolicy, index) in replicationPolicies: { + for (replicationPolicy, index) in (replicationPolicies ?? []): { name: '${uniqueString(deployment().name, location)}-RSV-Policy-${index}' params: { name: replicationPolicy.name recoveryVaultName: rsv.name - appConsistentFrequencyInMinutes: contains(replicationPolicy, 'appConsistentFrequencyInMinutes') - ? replicationPolicy.appConsistentFrequencyInMinutes - : 60 - crashConsistentFrequencyInMinutes: contains(replicationPolicy, 'crashConsistentFrequencyInMinutes') - ? replicationPolicy.crashConsistentFrequencyInMinutes - : 5 - multiVmSyncStatus: contains(replicationPolicy, 'multiVmSyncStatus') - ? replicationPolicy.multiVmSyncStatus - : 'Enable' - recoveryPointHistory: contains(replicationPolicy, 'recoveryPointHistory') - ? replicationPolicy.recoveryPointHistory - : 1440 + appConsistentFrequencyInMinutes: replicationPolicy.?appConsistentFrequencyInMinutes + crashConsistentFrequencyInMinutes: replicationPolicy.?crashConsistentFrequencyInMinutes + multiVmSyncStatus: replicationPolicy.?multiVmSyncStatus + recoveryPointHistory: replicationPolicy.?recoveryPointHistory } } ] @@ -209,29 +266,31 @@ module rsv_backupStorageConfiguration 'backup-storage-config/main.bicep' = if (! name: '${uniqueString(deployment().name, location)}-RSV-BackupStorageConfig' params: { recoveryVaultName: rsv.name - storageModelType: backupStorageConfig.storageModelType - crossRegionRestoreFlag: backupStorageConfig.crossRegionRestoreFlag + storageModelType: backupStorageConfig!.storageModelType + crossRegionRestoreFlag: backupStorageConfig!.crossRegionRestoreFlag } } -module rsv_backupFabric_protectionContainers 'backup-fabric/protection-container/main.bicep' = [ - for (protectionContainer, index) in protectionContainers: { - name: '${uniqueString(deployment().name, location)}-RSV-ProtectionContainers-${index}' +module rsv_backupFabric_protectionContainer_protectedItems 'backup-fabric/protection-container/protected-item/main.bicep' = [ + for (protectedItem, index) in (protectedItems ?? []): { + name: '${uniqueString(deployment().name, location)}-ProtectedItem-${index}' params: { recoveryVaultName: rsv.name - name: protectionContainer.name - sourceResourceId: protectionContainer.?sourceResourceId - friendlyName: protectionContainer.?friendlyName - backupManagementType: protectionContainer.?backupManagementType - containerType: protectionContainer.?containerType - protectedItems: contains(protectionContainer, 'protectedItems') ? protectionContainer.protectedItems : [] + name: protectedItem.name location: location + policyName: protectedItem.policyName + protectedItemType: protectedItem.protectedItemType + protectionContainerName: protectedItem.protectionContainerName + sourceResourceId: protectedItem.sourceResourceId } + dependsOn: [ + rsv_backupPolicies + ] } ] module rsv_backupPolicies 'backup-policy/main.bicep' = [ - for (backupPolicy, index) in backupPolicies: { + for (backupPolicy, index) in (backupPolicies ?? []): { name: '${uniqueString(deployment().name, location)}-RSV-BackupPolicy-${index}' params: { recoveryVaultName: rsv.name @@ -245,22 +304,14 @@ module rsv_backupConfig 'backup-config/main.bicep' = if (!empty(backupConfig)) { name: '${uniqueString(deployment().name, location)}-RSV-BackupConfig' params: { recoveryVaultName: rsv.name - name: contains(backupConfig, 'name') ? backupConfig.name : 'vaultconfig' - enhancedSecurityState: contains(backupConfig, 'enhancedSecurityState') - ? backupConfig.enhancedSecurityState - : 'Enabled' - resourceGuardOperationRequests: contains(backupConfig, 'resourceGuardOperationRequests') - ? backupConfig.resourceGuardOperationRequests - : [] - softDeleteFeatureState: contains(backupConfig, 'softDeleteFeatureState') - ? backupConfig.softDeleteFeatureState - : 'Enabled' - storageModelType: contains(backupConfig, 'storageModelType') ? backupConfig.storageModelType : 'GeoRedundant' - storageType: contains(backupConfig, 'storageType') ? backupConfig.storageType : 'GeoRedundant' - storageTypeState: contains(backupConfig, 'storageTypeState') ? backupConfig.storageTypeState : 'Locked' - isSoftDeleteFeatureStateEditable: contains(backupConfig, 'isSoftDeleteFeatureStateEditable') - ? backupConfig.isSoftDeleteFeatureStateEditable - : true + name: backupConfig.?name + enhancedSecurityState: backupConfig.?enhancedSecurityState + resourceGuardOperationRequests: backupConfig.?resourceGuardOperationRequests + softDeleteFeatureState: backupConfig.?softDeleteFeatureState + storageModelType: backupConfig.?storageModelType + storageType: backupConfig.?storageType + storageTypeState: backupConfig.?storageTypeState + isSoftDeleteFeatureStateEditable: backupConfig.?isSoftDeleteFeatureStateEditable } } @@ -269,11 +320,9 @@ module rsv_replicationAlertSettings 'replication-alert-setting/main.bicep' = if params: { name: 'defaultAlertSetting' recoveryVaultName: rsv.name - customEmailAddresses: contains(replicationAlertSettings, 'customEmailAddresses') - ? replicationAlertSettings.customEmailAddresses - : [] - locale: contains(replicationAlertSettings, 'locale') ? replicationAlertSettings.locale : '' - sendToOwners: contains(replicationAlertSettings, 'sendToOwners') ? replicationAlertSettings.sendToOwners : 'Send' + customEmailAddresses: replicationAlertSettings.?customEmailAddresses + locale: replicationAlertSettings.?locale + sendToOwners: replicationAlertSettings.?sendToOwners } } @@ -446,3 +495,201 @@ type privateEndpointOutputType = { @description('The IDs of the network interfaces associated with the private endpoint.') networkInterfaceResourceIds: string[] } + +@export() +@description('The type for redundancy settings.') +type redundancySettingsType = { + @description('Optional. Flag to show if Cross Region Restore is enabled on the Vault or not.') + crossRegionRestore: string? + + @description('Optional. The storage redundancy setting of a vault.') + standardTierStorageRedundancy: string? +} + +@export() +@description('The type for restore settings.') +type restoreSettingsType = { + @description('Required. The restore settings of the vault.') + crossSubscriptionRestoreSettings: { + @description('Required. The restore settings of the vault.') + crossSubscriptionRestoreState: string + } +} + +import { containerType } from 'replication-fabric/main.bicep' +@export() +@description('The type for replication fabrics.') +type replicationFabricType = { + @description('Optional. The name of the fabric.') + name: string? + + @description('Optional. The recovery location the fabric represents.') + location: string? + + @description('Optional. Replication containers to create.') + replicationContainers: containerType[]? +} + +@export() +@description('The type for replication policies.') +type replicationPolicyType = { + @description('Required. The name of the replication policy.') + name: string + + @description('Optional. The app consistent snapshot frequency (in minutes).') + appConsistentFrequencyInMinutes: int? + + @description('Optional. The crash consistent snapshot frequency (in minutes).') + crashConsistentFrequencyInMinutes: int? + + @description('Optional. A value indicating whether multi-VM sync has to be enabled.') + multiVmSyncStatus: ('Enable' | 'Disable')? + + @description('Optional. The duration in minutes until which the recovery points need to be stored.') + recoveryPointHistory: int? +} + +@export() +@description('The type for a backup storage config.') +type backupStorageConfigType = { + @description('Optional. The name of the backup storage config.') + name: string? + + @description('Optional. Change Vault Storage Type (Works if vault has not registered any backup instance).') + storageModelType: ('GeoRedundant' | 'LocallyRedundant' | 'ReadAccessGeoZoneRedundant' | 'ZoneRedundant')? + + @description('Optional. Opt in details of Cross Region Restore feature.') + crossRegionRestoreFlag: bool? +} + +@export() +@description('The type for a backup configuration.') +type backupConfigType = { + @description('Optional. Name of the Azure Recovery Service Vault Backup Policy.') + name: string? + + @description('Optional. Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations.') + enhancedSecurityState: ('Disabled' | 'Enabled')? + + @description('Optional. ResourceGuard Operation Requests.') + resourceGuardOperationRequests: object[]? + + @description('Optional. Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes.') + softDeleteFeatureState: ('Disabled' | 'Enabled')? + + @description('Optional. Storage type.') + storageModelType: ('GeoRedundant' | 'LocallyRedundant' | 'ReadAccessGeoZoneRedundant' | 'ZoneRedundant')? + + @description('Optional. Storage type.') + storageType: ('GeoRedundant' | 'LocallyRedundant' | 'ReadAccessGeoZoneRedundant' | 'ZoneRedundant')? + + @description('Optional. Once a machine is registered against a resource, the storageTypeState is always Locked.') + storageTypeState: ('Locked' | 'Unlocked')? + + @description('Optional. Is soft delete feature state editable.') + isSoftDeleteFeatureStateEditable: bool? +} + +@export() +@description('The type for replication alert settings') +type replicationAlertSettingsType = { + @description('Optional. The name of the replication Alert Setting.') + name: string? + + @description('Optional. The custom email address for sending emails.') + customEmailAddresses: string[]? + + @description('Optional. The locale for the email notification.') + locale: string? + + @description('Optional. The value indicating whether to send email to subscription administrator.') + sendToOwners: ('DoNotSend' | 'Send')? +} + +@export() +@description('The type for a protected item') +type protectedItemType = { + @description('Required. Name of the resource.') + name: string + + @description('Optional. Location for all resources.') + location: string? + + @description('Required. Name of the Azure Recovery Service Vault Protection Container.') + protectionContainerName: string + + @description('Required. The backup item type.') + protectedItemType: ( + | 'AzureFileShareProtectedItem' + | 'AzureVmWorkloadSAPAseDatabase' + | 'AzureVmWorkloadSAPHanaDatabase' + | 'AzureVmWorkloadSQLDatabase' + | 'DPMProtectedItem' + | 'GenericProtectedItem' + | 'MabFileFolderProtectedItem' + | 'Microsoft.ClassicCompute/virtualMachines' + | 'Microsoft.Compute/virtualMachines' + | 'Microsoft.Sql/servers/databases') + + @description('Required. The backup policy with which this item is backed up.') + policyName: string + + @description('Required. Resource ID of the resource to back up.') + sourceResourceId: string +} + +@export() +@description('The type of a backup policy.') +type backupPolicyType = { + @description('Required. Name of the Azure Recovery Service Vault Backup Policy.') + name: string + + @description('Required. Configuration of the Azure Recovery Service Vault Backup Policy.') + properties: object +} + +@export() +type monitoringSettingsType = { + @description('Optional. The alert settings.') + azureMonitorAlertSettings: { + @description('Optional. Enable / disable alerts for all failover issues.') + alertsForAllFailoverIssues: ('Enabled' | 'Disabled')? + + @description('Optional. Enable / disable alerts for all job failures.') + alertsForAllJobFailures: ('Enabled' | 'Disabled')? + + @description('Optional. Enable / disable alerts for all replication issues.') + alertsForAllReplicationIssues: ('Enabled' | 'Disabled')? + }? + + @description('Optional. The classic alert settings.') + classicAlertSettings: { + @description('Optional. Enable / disable alerts for critical operations.') + alertsForCriticalOperations: ('Enabled' | 'Disabled')? + + @description('Optional. Enable / disable email notifications for site recovery.') + emailNotificationsForSiteRecovery: ('Enabled' | 'Disabled')? + }? +} + +@export() +@description('The type for security settings.') +type securitySettingType = { + @description('Optional. Immutability settings of a vault.') + immutabilitySettings: { + @description('Required. The immmutability setting of the vault.') + state: ('Disabled' | 'Locked' | 'Unlocked') + }? + + @description('Optional. Soft delete settings of a vault.') + softDeleteSettings: { + @description('Required. The enhanced security state.') + enhancedSecurityState: ('AlwaysON' | 'Disabled' | 'Enabled' | 'Invalid') + + @description('Required. The soft delete retention period in days.') + softDeleteRetentionPeriodInDays: int + + @description('Required. The soft delete state.') + softDeleteState: ('AlwaysON' | 'Disabled' | 'Enabled' | 'Invalid') + }? +} diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index afcefb46ff..c162dc8dab 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "6598977512480723703" + "templateHash": "1404292748211985740" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault." @@ -75,6 +75,532 @@ "__bicep_export!": true } }, + "redundancySettingsType": { + "type": "object", + "properties": { + "crossRegionRestore": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Flag to show if Cross Region Restore is enabled on the Vault or not." + } + }, + "standardTierStorageRedundancy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The storage redundancy setting of a vault." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for redundancy settings." + } + }, + "restoreSettingsType": { + "type": "object", + "properties": { + "crossSubscriptionRestoreSettings": { + "type": "object", + "properties": { + "crossSubscriptionRestoreState": { + "type": "string", + "metadata": { + "description": "Required. The restore settings of the vault." + } + } + }, + "metadata": { + "description": "Required. The restore settings of the vault." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for restore settings." + } + }, + "replicationFabricType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the fabric." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The recovery location the fabric represents." + } + }, + "replicationContainers": { + "type": "array", + "items": { + "$ref": "#/definitions/containerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Replication containers to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for replication fabrics." + } + }, + "replicationPolicyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication policy." + } + }, + "appConsistentFrequencyInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The app consistent snapshot frequency (in minutes)." + } + }, + "crashConsistentFrequencyInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The crash consistent snapshot frequency (in minutes)." + } + }, + "multiVmSyncStatus": { + "type": "string", + "allowedValues": [ + "Disable", + "Enable" + ], + "nullable": true, + "metadata": { + "description": "Optional. A value indicating whether multi-VM sync has to be enabled." + } + }, + "recoveryPointHistory": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The duration in minutes until which the recovery points need to be stored." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for replication policies." + } + }, + "backupStorageConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the backup storage config." + } + }, + "storageModelType": { + "type": "string", + "allowedValues": [ + "GeoRedundant", + "LocallyRedundant", + "ReadAccessGeoZoneRedundant", + "ZoneRedundant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Change Vault Storage Type (Works if vault has not registered any backup instance)." + } + }, + "crossRegionRestoreFlag": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Opt in details of Cross Region Restore feature." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a backup storage config." + } + }, + "backupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the Azure Recovery Service Vault Backup Policy." + } + }, + "enhancedSecurityState": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations." + } + }, + "resourceGuardOperationRequests": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. ResourceGuard Operation Requests." + } + }, + "softDeleteFeatureState": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes." + } + }, + "storageModelType": { + "type": "string", + "allowedValues": [ + "GeoRedundant", + "LocallyRedundant", + "ReadAccessGeoZoneRedundant", + "ZoneRedundant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Storage type." + } + }, + "storageType": { + "type": "string", + "allowedValues": [ + "GeoRedundant", + "LocallyRedundant", + "ReadAccessGeoZoneRedundant", + "ZoneRedundant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Storage type." + } + }, + "storageTypeState": { + "type": "string", + "allowedValues": [ + "Locked", + "Unlocked" + ], + "nullable": true, + "metadata": { + "description": "Optional. Once a machine is registered against a resource, the storageTypeState is always Locked." + } + }, + "isSoftDeleteFeatureStateEditable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is soft delete feature state editable." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a backup configuration." + } + }, + "replicationAlertSettingsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication Alert Setting." + } + }, + "customEmailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The custom email address for sending emails." + } + }, + "locale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale for the email notification." + } + }, + "sendToOwners": { + "type": "string", + "allowedValues": [ + "DoNotSend", + "Send" + ], + "nullable": true, + "metadata": { + "description": "Optional. The value indicating whether to send email to subscription administrator." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for replication alert settings" + } + }, + "protectedItemType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the resource." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "protectionContainerName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Recovery Service Vault Protection Container." + } + }, + "protectedItemType": { + "type": "string", + "allowedValues": [ + "AzureFileShareProtectedItem", + "AzureVmWorkloadSAPAseDatabase", + "AzureVmWorkloadSAPHanaDatabase", + "AzureVmWorkloadSQLDatabase", + "DPMProtectedItem", + "GenericProtectedItem", + "MabFileFolderProtectedItem", + "Microsoft.ClassicCompute/virtualMachines", + "Microsoft.Compute/virtualMachines", + "Microsoft.Sql/servers/databases" + ], + "metadata": { + "description": "Required. The backup item type." + } + }, + "policyName": { + "type": "string", + "metadata": { + "description": "Required. The backup policy with which this item is backed up." + } + }, + "sourceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource to back up." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a protected item" + } + }, + "backupPolicyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Recovery Service Vault Backup Policy." + } + }, + "properties": { + "type": "object", + "metadata": { + "description": "Required. Configuration of the Azure Recovery Service Vault Backup Policy." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a backup policy." + } + }, + "monitoringSettingsType": { + "type": "object", + "properties": { + "azureMonitorAlertSettings": { + "type": "object", + "properties": { + "alertsForAllFailoverIssues": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable / disable alerts for all failover issues." + } + }, + "alertsForAllJobFailures": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable / disable alerts for all job failures." + } + }, + "alertsForAllReplicationIssues": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable / disable alerts for all replication issues." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The alert settings." + } + }, + "classicAlertSettings": { + "type": "object", + "properties": { + "alertsForCriticalOperations": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable / disable alerts for critical operations." + } + }, + "emailNotificationsForSiteRecovery": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable / disable email notifications for site recovery." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The classic alert settings." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "securitySettingType": { + "type": "object", + "properties": { + "immutabilitySettings": { + "type": "object", + "properties": { + "state": { + "type": "string", + "allowedValues": [ + "Disabled", + "Locked", + "Unlocked" + ], + "metadata": { + "description": "Required. The immmutability setting of the vault." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Immutability settings of a vault." + } + }, + "softDeleteSettings": { + "type": "object", + "properties": { + "enhancedSecurityState": { + "type": "string", + "allowedValues": [ + "AlwaysON", + "Disabled", + "Enabled", + "Invalid" + ], + "metadata": { + "description": "Required. The enhanced security state." + } + }, + "softDeleteRetentionPeriodInDays": { + "type": "int", + "metadata": { + "description": "Required. The soft delete retention period in days." + } + }, + "softDeleteState": { + "type": "string", + "allowedValues": [ + "AlwaysON", + "Disabled", + "Enabled", + "Invalid" + ], + "metadata": { + "description": "Required. The soft delete state." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Soft delete settings of a vault." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for security settings." + } + }, "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { @@ -184,6 +710,130 @@ } } }, + "_2.mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "description": "The type for protection container mappings.", + "__bicep_imported_from!": { + "sourceTemplate": "replication-fabric/replication-protection-container/main.bicep" + } + } + }, + "containerType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication container." + } + }, + "mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.mappingType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Replication containers mappings to create." + } + } + }, + "metadata": { + "description": "The type for a replication protection container.", + "__bicep_imported_from!": { + "sourceTemplate": "replication-fabric/main.bicep" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "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 version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -590,8 +1240,8 @@ } }, "backupStorageConfig": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/backupStorageConfigType", + "nullable": true, "metadata": { "description": "Optional. The storage configuration for the Azure Recovery Service Vault." } @@ -612,42 +1262,54 @@ }, "backupPolicies": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/backupPolicyType" + }, + "nullable": true, "metadata": { "description": "Optional. List of all backup policies." } }, "backupConfig": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/backupConfigType", + "nullable": true, "metadata": { "description": "Optional. The backup configuration." } }, - "protectionContainers": { + "protectedItems": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/protectedItemType" + }, + "nullable": true, "metadata": { "description": "Optional. List of all protection containers." } }, "replicationFabrics": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/replicationFabricType" + }, + "nullable": true, "metadata": { "description": "Optional. List of all replication fabrics." } }, "replicationPolicies": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/replicationPolicyType" + }, + "nullable": true, "metadata": { "description": "Optional. List of all replication policies." } }, "replicationAlertSettings": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/replicationAlertSettingsType", + "nullable": true, "metadata": { "description": "Optional. Replication alert settings." } @@ -704,15 +1366,15 @@ } }, "monitoringSettings": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/monitoringSettingsType", + "nullable": true, "metadata": { "description": "Optional. Monitoring Settings of the vault." } }, "securitySettings": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/securitySettingType", + "nullable": true, "metadata": { "description": "Optional. Security Settings of the vault." } @@ -727,6 +1389,27 @@ "metadata": { "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled." } + }, + "redundancySettings": { + "$ref": "#/definitions/redundancySettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The redundancy settings of the vault." + } + }, + "restoreSettings": { + "$ref": "#/definitions/restoreSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The restore settings of the vault." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } } }, "variables": { @@ -754,6 +1437,15 @@ } }, "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'))]" + }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", @@ -774,9 +1466,27 @@ } } }, + "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'), '/'))]" + }, "rsv": { "type": "Microsoft.RecoveryServices/vaults", - "apiVersion": "2023-01-01", + "apiVersion": "2024-04-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -786,10 +1496,16 @@ "tier": "Standard" }, "properties": { - "monitoringSettings": "[if(not(empty(parameters('monitoringSettings'))), parameters('monitoringSettings'), null())]", - "securitySettings": "[if(not(empty(parameters('securitySettings'))), parameters('securitySettings'), null())]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]" - } + "monitoringSettings": "[if(not(empty(parameters('monitoringSettings'))), createObject('azureMonitorAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'))), createObject('alertsForAllFailoverIssues', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllFailoverIssues'), 'Enabled'), 'alertsForAllJobFailures', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllJobFailures'), 'Enabled'), 'alertsForAllReplicationIssues', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllReplicationIssues'), 'Enabled')), null()), 'classicAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'classicAlertSettings'))), createObject('alertsForCriticalOperations', coalesce(tryGet(parameters('monitoringSettings').classicAlertSettings, 'alertsForCriticalOperations'), 'Enabled'), 'emailNotificationsForSiteRecovery', coalesce(tryGet(parameters('monitoringSettings').classicAlertSettings, 'emailNotificationsForSiteRecovery'), 'Enabled')), null())), null())]", + "securitySettings": "[parameters('securitySettings')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "redundancySettings": "[parameters('redundancySettings')]", + "restoreSettings": "[parameters('restoreSettings')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('infrastructureEncryption', 'Enabled', 'kekIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), createObject('useSystemAssignedIdentity', empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))), 'keyVaultProperties', createObject('keyUri', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey" + ] }, "rsv_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", @@ -871,7 +1587,7 @@ "rsv_replicationFabrics": { "copy": { "name": "rsv_replicationFabrics", - "count": "[length(parameters('replicationFabrics'))]" + "count": "[length(coalesce(parameters('replicationFabrics'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -885,24 +1601,109 @@ "recoveryVaultName": { "value": "[parameters('name')]" }, - "name": "[if(contains(parameters('replicationFabrics')[copyIndex()], 'name'), createObject('value', parameters('replicationFabrics')[copyIndex()].name), createObject('value', parameters('replicationFabrics')[copyIndex()].location))]", + "name": { + "value": "[tryGet(coalesce(parameters('replicationFabrics'), createArray())[copyIndex()], 'name')]" + }, "location": { - "value": "[parameters('replicationFabrics')[copyIndex()].location]" + "value": "[coalesce(parameters('replicationFabrics'), createArray())[copyIndex()].location]" }, - "replicationContainers": "[if(contains(parameters('replicationFabrics')[copyIndex()], 'replicationContainers'), createObject('value', parameters('replicationFabrics')[copyIndex()].replicationContainers), createObject('value', createArray()))]" + "replicationContainers": { + "value": "[tryGet(coalesce(parameters('replicationFabrics'), createArray())[copyIndex()], 'replicationContainers')]" + } }, "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.33.13.18514", - "templateHash": "16265039904717699288" + "templateHash": "8049192542960910465" }, "name": "Recovery Services Vault Replication Fabrics", "description": "This module deploys a Replication Fabric for Azure to Azure disaster recovery scenario of Azure Site Recovery.\n\n> Note: this module currently support only the `instanceType: 'Azure'` scenario." }, + "definitions": { + "containerType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication container." + } + }, + "mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/mappingType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Replication containers mappings to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a replication protection container." + } + }, + "mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "description": "The type for protection container mappings.", + "__bicep_imported_from!": { + "sourceTemplate": "replication-protection-container/main.bicep" + } + } + } + }, "parameters": { "recoveryVaultName": { "type": "string", @@ -926,14 +1727,23 @@ }, "replicationContainers": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/containerType" + }, + "nullable": true, "metadata": { "description": "Optional. Replication containers to create." } } }, - "resources": [ - { + "resources": { + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationFabric": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", @@ -944,10 +1754,10 @@ } } }, - { + "fabric_replicationContainers": { "copy": { "name": "fabric_replicationContainers", - "count": "[length(parameters('replicationContainers'))]" + "count": "[length(coalesce(parameters('replicationContainers'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -959,7 +1769,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('replicationContainers')[copyIndex()].name]" + "value": "[coalesce(parameters('replicationContainers'), createArray())[copyIndex()].name]" }, "recoveryVaultName": { "value": "[parameters('recoveryVaultName')]" @@ -967,22 +1777,76 @@ "replicationFabricName": { "value": "[parameters('name')]" }, - "replicationContainerMappings": { - "value": "[tryGet(parameters('replicationContainers')[copyIndex()], 'replicationContainerMappings')]" + "mappings": { + "value": "[tryGet(coalesce(parameters('replicationContainers'), createArray())[copyIndex()], 'mappings')]" } }, "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.33.13.18514", - "templateHash": "3485070905266123603" + "templateHash": "852813454503469785" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." }, + "definitions": { + "mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for protection container mappings." + } + } + }, "parameters": { "recoveryVaultName": { "type": "string", @@ -1002,16 +1866,31 @@ "description": "Required. The name of the replication container." } }, - "replicationContainerMappings": { + "mappings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/mappingType" + }, + "nullable": true, "metadata": { "description": "Optional. Replication containers mappings to create." } } }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]", @@ -1023,10 +1902,10 @@ ] } }, - { + "fabric_container_containerMappings": { "copy": { "name": "fabric_container_containerMappings", - "count": "[length(parameters('replicationContainerMappings'))]" + "count": "[length(coalesce(parameters('mappings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1038,13 +1917,13 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'name')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'name')]" }, - "policyId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyId')]" + "policyResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyResourceId')]" }, "policyName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyName')]" }, "recoveryVaultName": { "value": "[parameters('recoveryVaultName')]" @@ -1055,24 +1934,25 @@ "sourceProtectionContainerName": { "value": "[parameters('name')]" }, - "targetProtectionContainerId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetProtectionContainerId')]" + "targetProtectionContainerResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetProtectionContainerResourceId')]" }, "targetContainerFabricName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerFabricName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerFabricName')]" }, "targetContainerName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerName')]" } }, "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.33.13.18514", - "templateHash": "6423347652173335680" + "templateHash": "17160081039503517469" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." @@ -1096,9 +1976,9 @@ "description": "Conditional. The name of the parent source Replication container. Required if the template is used in a standalone deployment." } }, - "targetProtectionContainerId": { + "targetProtectionContainerResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." } @@ -1107,17 +1987,17 @@ "type": "string", "defaultValue": "[parameters('replicationFabricName')]", "metadata": { - "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, "targetContainerName": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, - "policyId": { + "policyResourceId": { "type": "string", "defaultValue": "", "metadata": { @@ -1128,7 +2008,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the replication policy. Will be ignored if policyId is also specified." + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." } }, "name": { @@ -1140,38 +2020,56 @@ } }, "variables": { - "policyResourceId": "[if(not(equals(parameters('policyId'), '')), parameters('policyId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", - "targetProtectionContainerResourceId": "[if(not(equals(parameters('targetProtectionContainerId'), '')), parameters('targetProtectionContainerId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", - "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('targetProtectionContainerResourceId'), '/')[10]))]" + "calcPolicyResourceId": "[if(not(empty(parameters('policyResourceId'))), parameters('policyResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", + "calcTargetProtectionContainerResourceId": "[if(not(empty(parameters('targetProtectionContainerResourceId'))), parameters('targetProtectionContainerResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", + "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('calcTargetProtectionContainerResourceId'), '/')[10]))]" }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric::replicationContainer": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'))]" + }, + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]", "properties": { - "targetProtectionContainerId": "[variables('targetProtectionContainerResourceId')]", - "policyId": "[variables('policyResourceId')]", + "targetProtectionContainerId": "[variables('calcTargetProtectionContainerResourceId')]", + "policyId": "[variables('calcPolicyResourceId')]", "providerSpecificInput": { "instanceType": "A2A" } } } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" + "value": "[variables('mappingName')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[0], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[1], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[2], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[3])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" }, "resourceGroupName": { "type": "string", @@ -1184,24 +2082,24 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "replicationContainer" ] } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -1214,24 +2112,24 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1])]" + "replicationFabric" ] } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication fabric." }, - "value": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication fabric." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', parameters('recoveryVaultName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -1251,7 +2149,7 @@ "rsv_replicationPolicies": { "copy": { "name": "rsv_replicationPolicies", - "count": "[length(parameters('replicationPolicies'))]" + "count": "[length(coalesce(parameters('replicationPolicies'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1263,15 +2161,23 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('replicationPolicies')[copyIndex()].name]" + "value": "[coalesce(parameters('replicationPolicies'), createArray())[copyIndex()].name]" }, "recoveryVaultName": { "value": "[parameters('name')]" }, - "appConsistentFrequencyInMinutes": "[if(contains(parameters('replicationPolicies')[copyIndex()], 'appConsistentFrequencyInMinutes'), createObject('value', parameters('replicationPolicies')[copyIndex()].appConsistentFrequencyInMinutes), createObject('value', 60))]", - "crashConsistentFrequencyInMinutes": "[if(contains(parameters('replicationPolicies')[copyIndex()], 'crashConsistentFrequencyInMinutes'), createObject('value', parameters('replicationPolicies')[copyIndex()].crashConsistentFrequencyInMinutes), createObject('value', 5))]", - "multiVmSyncStatus": "[if(contains(parameters('replicationPolicies')[copyIndex()], 'multiVmSyncStatus'), createObject('value', parameters('replicationPolicies')[copyIndex()].multiVmSyncStatus), createObject('value', 'Enable'))]", - "recoveryPointHistory": "[if(contains(parameters('replicationPolicies')[copyIndex()], 'recoveryPointHistory'), createObject('value', parameters('replicationPolicies')[copyIndex()].recoveryPointHistory), createObject('value', 1440))]" + "appConsistentFrequencyInMinutes": { + "value": "[tryGet(coalesce(parameters('replicationPolicies'), createArray())[copyIndex()], 'appConsistentFrequencyInMinutes')]" + }, + "crashConsistentFrequencyInMinutes": { + "value": "[tryGet(coalesce(parameters('replicationPolicies'), createArray())[copyIndex()], 'crashConsistentFrequencyInMinutes')]" + }, + "multiVmSyncStatus": { + "value": "[tryGet(coalesce(parameters('replicationPolicies'), createArray())[copyIndex()], 'multiVmSyncStatus')]" + }, + "recoveryPointHistory": { + "value": "[tryGet(coalesce(parameters('replicationPolicies'), createArray())[copyIndex()], 'recoveryPointHistory')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1484,14 +2390,14 @@ "rsv" ] }, - "rsv_backupFabric_protectionContainers": { + "rsv_backupFabric_protectionContainer_protectedItems": { "copy": { - "name": "rsv_backupFabric_protectionContainers", - "count": "[length(parameters('protectionContainers'))]" + "name": "rsv_backupFabric_protectionContainer_protectedItems", + "count": "[length(coalesce(parameters('protectedItems'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-RSV-ProtectionContainers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-ProtectedItem-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1502,49 +2408,53 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('protectionContainers')[copyIndex()].name]" + "value": "[coalesce(parameters('protectedItems'), createArray())[copyIndex()].name]" }, - "sourceResourceId": { - "value": "[tryGet(parameters('protectionContainers')[copyIndex()], 'sourceResourceId')]" + "location": { + "value": "[parameters('location')]" }, - "friendlyName": { - "value": "[tryGet(parameters('protectionContainers')[copyIndex()], 'friendlyName')]" + "policyName": { + "value": "[coalesce(parameters('protectedItems'), createArray())[copyIndex()].policyName]" }, - "backupManagementType": { - "value": "[tryGet(parameters('protectionContainers')[copyIndex()], 'backupManagementType')]" + "protectedItemType": { + "value": "[coalesce(parameters('protectedItems'), createArray())[copyIndex()].protectedItemType]" }, - "containerType": { - "value": "[tryGet(parameters('protectionContainers')[copyIndex()], 'containerType')]" + "protectionContainerName": { + "value": "[coalesce(parameters('protectedItems'), createArray())[copyIndex()].protectionContainerName]" }, - "protectedItems": "[if(contains(parameters('protectionContainers')[copyIndex()], 'protectedItems'), createObject('value', parameters('protectionContainers')[copyIndex()].protectedItems), createObject('value', createArray()))]", - "location": { - "value": "[parameters('location')]" + "sourceResourceId": { + "value": "[coalesce(parameters('protectedItems'), createArray())[copyIndex()].sourceResourceId]" } }, "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.33.13.18514", - "templateHash": "11151484631677356109" + "templateHash": "14694277488209426813" }, - "name": "Recovery Services Vault Protection Container", - "description": "This module deploys a Recovery Services Vault Protection Container." + "name": "Recovery Service Vaults Protection Container Protected Item", + "description": "This module deploys a Recovery Services Vault Protection Container Protected Item." }, "parameters": { - "recoveryVaultName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." + "description": "Required. Name of the resource." } }, - "name": { + "protectionContainerName": { + "type": "string", + "metadata": { + "description": "Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment." + } + }, + "recoveryVaultName": { "type": "string", "metadata": { - "description": "Required. Name of the Azure Recovery Service Vault Protection Container." + "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." } }, "location": { @@ -1554,257 +2464,84 @@ "description": "Optional. Location for all resources." } }, - "backupManagementType": { + "protectedItemType": { "type": "string", - "nullable": true, "allowedValues": [ - "AzureBackupServer", - "AzureIaasVM", - "AzureSql", - "AzureStorage", - "AzureWorkload", - "DPM", - "DefaultBackup", - "Invalid", - "MAB" + "AzureFileShareProtectedItem", + "AzureVmWorkloadSAPAseDatabase", + "AzureVmWorkloadSAPHanaDatabase", + "AzureVmWorkloadSQLDatabase", + "DPMProtectedItem", + "GenericProtectedItem", + "MabFileFolderProtectedItem", + "Microsoft.ClassicCompute/virtualMachines", + "Microsoft.Compute/virtualMachines", + "Microsoft.Sql/servers/databases" ], "metadata": { - "description": "Optional. Backup management type to execute the current Protection Container job." - } - }, - "sourceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the target resource for the Protection Container." + "description": "Required. The backup item type." } }, - "friendlyName": { + "policyName": { "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Friendly name of the Protection Container." - } - }, - "protectedItems": { - "type": "array", - "defaultValue": [], "metadata": { - "description": "Optional. Protected items to register in the container." + "description": "Required. The backup policy with which this item is backed up." } }, - "containerType": { + "sourceResourceId": { "type": "string", - "nullable": true, - "allowedValues": [ - "AzureBackupServerContainer", - "AzureSqlContainer", - "GenericContainer", - "Microsoft.ClassicCompute/virtualMachines", - "Microsoft.Compute/virtualMachines", - "SQLAGWorkLoadContainer", - "StorageContainer", - "VMAppContainer", - "Windows" - ], "metadata": { - "description": "Optional. Type of the container." + "description": "Required. Resource ID of the resource to back up." } } }, - "resources": { - "protectionContainer": { - "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers", - "apiVersion": "2023-01-01", - "name": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems", + "apiVersion": "2024-10-01", + "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]", + "location": "[parameters('location')]", "properties": { - "sourceResourceId": "[parameters('sourceResourceId')]", - "friendlyName": "[parameters('friendlyName')]", - "backupManagementType": "[parameters('backupManagementType')]", - "containerType": "[if(not(empty(parameters('containerType'))), parameters('containerType'), null())]" + "protectedItemType": "[parameters('protectedItemType')]", + "policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryVaultName'), parameters('policyName'))]", + "sourceResourceId": "[parameters('sourceResourceId')]" } - }, - "protectionContainer_protectedItems": { - "copy": { - "name": "protectionContainer_protectedItems", - "count": "[length(parameters('protectedItems'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-ProtectedItem-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "policyId": { - "value": "[parameters('protectedItems')[copyIndex()].policyId]" - }, - "name": { - "value": "[parameters('protectedItems')[copyIndex()].name]" - }, - "protectedItemType": { - "value": "[parameters('protectedItems')[copyIndex()].protectedItemType]" - }, - "protectionContainerName": { - "value": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]" - }, - "recoveryVaultName": { - "value": "[parameters('recoveryVaultName')]" - }, - "sourceResourceId": { - "value": "[parameters('protectedItems')[copyIndex()].sourceResourceId]" - }, - "location": { - "value": "[parameters('location')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "16309659094506585930" - }, - "name": "Recovery Service Vaults Protection Container Protected Item", - "description": "This module deploys a Recovery Services Vault Protection Container Protected Item." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the resource." - } - }, - "protectionContainerName": { - "type": "string", - "metadata": { - "description": "Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment." - } - }, - "recoveryVaultName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "protectedItemType": { - "type": "string", - "allowedValues": [ - "AzureFileShareProtectedItem", - "AzureVmWorkloadSAPAseDatabase", - "AzureVmWorkloadSAPHanaDatabase", - "AzureVmWorkloadSQLDatabase", - "DPMProtectedItem", - "GenericProtectedItem", - "MabFileFolderProtectedItem", - "Microsoft.ClassicCompute/virtualMachines", - "Microsoft.Compute/virtualMachines", - "Microsoft.Sql/servers/databases" - ], - "metadata": { - "description": "Required. The backup item type." - } - }, - "policyId": { - "type": "string", - "metadata": { - "description": "Required. ID of the backup policy with which this item is backed up." - } - }, - "sourceResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the resource to back up." - } - } - }, - "resources": [ - { - "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems", - "apiVersion": "2023-01-01", - "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]", - "location": "[parameters('location')]", - "properties": { - "protectedItemType": "[parameters('protectedItemType')]", - "policyId": "[parameters('policyId')]", - "sourceResourceId": "[parameters('sourceResourceId')]" - } - } - ], - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Resource Group the protected item was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the protected item." - }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems', split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[2], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[3])]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The Name of the protected item." - }, - "value": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]" - } - } - } - }, - "dependsOn": [ - "protectionContainer" - ] } - }, + ], "outputs": { "resourceGroupName": { "type": "string", "metadata": { - "description": "The name of the Resource Group the Protection Container was created in." + "description": "The name of the Resource Group the protected item was created in." }, "value": "[resourceGroup().name]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the Protection Container." + "description": "The resource ID of the protected item." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers', split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[2])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems', split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[2], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[3])]" }, "name": { "type": "string", "metadata": { - "description": "The Name of the Protection Container." + "description": "The Name of the protected item." }, - "value": "[format('{0}/Azure/{1}', parameters('recoveryVaultName'), parameters('name'))]" + "value": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]" } } } }, "dependsOn": [ - "rsv" + "rsv", + "rsv_backupPolicies" ] }, "rsv_backupPolicies": { "copy": { "name": "rsv_backupPolicies", - "count": "[length(parameters('backupPolicies'))]" + "count": "[length(coalesce(parameters('backupPolicies'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1819,10 +2556,10 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('backupPolicies')[copyIndex()].name]" + "value": "[coalesce(parameters('backupPolicies'), createArray())[copyIndex()].name]" }, "properties": { - "value": "[parameters('backupPolicies')[copyIndex()].properties]" + "value": "[coalesce(parameters('backupPolicies'), createArray())[copyIndex()].properties]" } }, "template": { @@ -1832,7 +2569,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "992831565851796181" + "templateHash": "18409788320626401161" }, "name": "Recovery Services Vault Backup Policies", "description": "This module deploys a Recovery Services Vault Backup Policy." @@ -1860,7 +2597,7 @@ "resources": [ { "type": "Microsoft.RecoveryServices/vaults/backupPolicies", - "apiVersion": "2023-01-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", "properties": "[parameters('properties')]" } @@ -1908,14 +2645,30 @@ "recoveryVaultName": { "value": "[parameters('name')]" }, - "name": "[if(contains(parameters('backupConfig'), 'name'), createObject('value', parameters('backupConfig').name), createObject('value', 'vaultconfig'))]", - "enhancedSecurityState": "[if(contains(parameters('backupConfig'), 'enhancedSecurityState'), createObject('value', parameters('backupConfig').enhancedSecurityState), createObject('value', 'Enabled'))]", - "resourceGuardOperationRequests": "[if(contains(parameters('backupConfig'), 'resourceGuardOperationRequests'), createObject('value', parameters('backupConfig').resourceGuardOperationRequests), createObject('value', createArray()))]", - "softDeleteFeatureState": "[if(contains(parameters('backupConfig'), 'softDeleteFeatureState'), createObject('value', parameters('backupConfig').softDeleteFeatureState), createObject('value', 'Enabled'))]", - "storageModelType": "[if(contains(parameters('backupConfig'), 'storageModelType'), createObject('value', parameters('backupConfig').storageModelType), createObject('value', 'GeoRedundant'))]", - "storageType": "[if(contains(parameters('backupConfig'), 'storageType'), createObject('value', parameters('backupConfig').storageType), createObject('value', 'GeoRedundant'))]", - "storageTypeState": "[if(contains(parameters('backupConfig'), 'storageTypeState'), createObject('value', parameters('backupConfig').storageTypeState), createObject('value', 'Locked'))]", - "isSoftDeleteFeatureStateEditable": "[if(contains(parameters('backupConfig'), 'isSoftDeleteFeatureStateEditable'), createObject('value', parameters('backupConfig').isSoftDeleteFeatureStateEditable), createObject('value', true()))]" + "name": { + "value": "[tryGet(parameters('backupConfig'), 'name')]" + }, + "enhancedSecurityState": { + "value": "[tryGet(parameters('backupConfig'), 'enhancedSecurityState')]" + }, + "resourceGuardOperationRequests": { + "value": "[tryGet(parameters('backupConfig'), 'resourceGuardOperationRequests')]" + }, + "softDeleteFeatureState": { + "value": "[tryGet(parameters('backupConfig'), 'softDeleteFeatureState')]" + }, + "storageModelType": { + "value": "[tryGet(parameters('backupConfig'), 'storageModelType')]" + }, + "storageType": { + "value": "[tryGet(parameters('backupConfig'), 'storageType')]" + }, + "storageTypeState": { + "value": "[tryGet(parameters('backupConfig'), 'storageTypeState')]" + }, + "isSoftDeleteFeatureStateEditable": { + "value": "[tryGet(parameters('backupConfig'), 'isSoftDeleteFeatureStateEditable')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2079,18 +2832,25 @@ "recoveryVaultName": { "value": "[parameters('name')]" }, - "customEmailAddresses": "[if(contains(parameters('replicationAlertSettings'), 'customEmailAddresses'), createObject('value', parameters('replicationAlertSettings').customEmailAddresses), createObject('value', createArray()))]", - "locale": "[if(contains(parameters('replicationAlertSettings'), 'locale'), createObject('value', parameters('replicationAlertSettings').locale), createObject('value', ''))]", - "sendToOwners": "[if(contains(parameters('replicationAlertSettings'), 'sendToOwners'), createObject('value', parameters('replicationAlertSettings').sendToOwners), createObject('value', 'Send'))]" + "customEmailAddresses": { + "value": "[tryGet(parameters('replicationAlertSettings'), 'customEmailAddresses')]" + }, + "locale": { + "value": "[tryGet(parameters('replicationAlertSettings'), 'locale')]" + }, + "sendToOwners": { + "value": "[tryGet(parameters('replicationAlertSettings'), 'sendToOwners')]" + } }, "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.33.13.18514", - "templateHash": "3897872700976586621" + "templateHash": "13714844551703046217" }, "name": "Recovery Services Vault Replication Alert Settings", "description": "This module deploys a Recovery Services Vault Replication Alert Settings." @@ -2111,9 +2871,12 @@ }, "customEmailAddresses": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. Comma separated list of custom email address for sending alert emails." + "description": "Optional. The custom email address for sending emails." } }, "locale": { @@ -2135,18 +2898,24 @@ } } }, - "resources": [ - { + "resources": { + "recoveryVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2023-01-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationAlertSettings": { "type": "Microsoft.RecoveryServices/vaults/replicationAlertSettings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", "properties": { - "customEmailAddresses": "[if(not(empty(parameters('customEmailAddresses'))), parameters('customEmailAddresses'), null())]", + "customEmailAddresses": "[parameters('customEmailAddresses')]", "locale": "[parameters('locale')]", "sendToOwners": "[parameters('sendToOwners')]" } } - ], + }, "outputs": { "name": { "type": "string", @@ -2960,14 +3729,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('rsv', '2023-01-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('rsv', '2024-04-01', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('rsv', '2023-01-01', 'full').location]" + "value": "[reference('rsv', '2024-04-01', 'full').location]" }, "privateEndpoints": { "type": "array", diff --git a/avm/res/recovery-services/vault/replication-alert-setting/README.md b/avm/res/recovery-services/vault/replication-alert-setting/README.md index 711a738ed4..5bceb91ca6 100644 --- a/avm/res/recovery-services/vault/replication-alert-setting/README.md +++ b/avm/res/recovery-services/vault/replication-alert-setting/README.md @@ -26,7 +26,7 @@ This module deploys a Recovery Services Vault Replication Alert Settings. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`customEmailAddresses`](#parameter-customemailaddresses) | array | Comma separated list of custom email address for sending alert emails. | +| [`customEmailAddresses`](#parameter-customemailaddresses) | array | The custom email address for sending emails. | | [`locale`](#parameter-locale) | string | The locale for the email notification. | | [`name`](#parameter-name) | string | The name of the replication Alert Setting. | | [`sendToOwners`](#parameter-sendtoowners) | string | The value indicating whether to send email to subscription administrator. | @@ -40,11 +40,10 @@ The name of the parent Azure Recovery Service Vault. Required if the template is ### Parameter: `customEmailAddresses` -Comma separated list of custom email address for sending alert emails. +The custom email address for sending emails. - Required: No - Type: array -- Default: `[]` ### Parameter: `locale` diff --git a/avm/res/recovery-services/vault/replication-alert-setting/main.bicep b/avm/res/recovery-services/vault/replication-alert-setting/main.bicep index a92f9ff0ca..33dfed3314 100644 --- a/avm/res/recovery-services/vault/replication-alert-setting/main.bicep +++ b/avm/res/recovery-services/vault/replication-alert-setting/main.bicep @@ -7,8 +7,8 @@ param recoveryVaultName string @description('Optional. The name of the replication Alert Setting.') param name string = 'defaultAlertSetting' -@description('Optional. Comma separated list of custom email address for sending alert emails.') -param customEmailAddresses array = [] +@description('Optional. The custom email address for sending emails.') +param customEmailAddresses string[]? @description('Optional. The locale for the email notification.') param locale string = '' @@ -28,7 +28,7 @@ resource replicationAlertSettings 'Microsoft.RecoveryServices/vaults/replication name: name parent: recoveryVault properties: { - customEmailAddresses: !empty(customEmailAddresses) ? customEmailAddresses : null + customEmailAddresses: customEmailAddresses locale: locale sendToOwners: sendToOwners } diff --git a/avm/res/recovery-services/vault/replication-alert-setting/main.json b/avm/res/recovery-services/vault/replication-alert-setting/main.json index 842682439c..8f3cc16a47 100644 --- a/avm/res/recovery-services/vault/replication-alert-setting/main.json +++ b/avm/res/recovery-services/vault/replication-alert-setting/main.json @@ -1,11 +1,12 @@ { "$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.32.4.45862", - "templateHash": "13751902698582597636" + "version": "0.33.13.18514", + "templateHash": "13714844551703046217" }, "name": "Recovery Services Vault Replication Alert Settings", "description": "This module deploys a Recovery Services Vault Replication Alert Settings." @@ -26,9 +27,12 @@ }, "customEmailAddresses": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. Comma separated list of custom email address for sending alert emails." + "description": "Optional. The custom email address for sending emails." } }, "locale": { @@ -50,18 +54,24 @@ } } }, - "resources": [ - { + "resources": { + "recoveryVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2023-01-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationAlertSettings": { "type": "Microsoft.RecoveryServices/vaults/replicationAlertSettings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", "properties": { - "customEmailAddresses": "[if(not(empty(parameters('customEmailAddresses'))), parameters('customEmailAddresses'), null())]", + "customEmailAddresses": "[parameters('customEmailAddresses')]", "locale": "[parameters('locale')]", "sendToOwners": "[parameters('sendToOwners')]" } } - ], + }, "outputs": { "name": { "type": "string", diff --git a/avm/res/recovery-services/vault/replication-fabric/README.md b/avm/res/recovery-services/vault/replication-fabric/README.md index c363361733..1ddb4829bf 100644 --- a/avm/res/recovery-services/vault/replication-fabric/README.md +++ b/avm/res/recovery-services/vault/replication-fabric/README.md @@ -63,7 +63,85 @@ Replication containers to create. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-replicationcontainersname) | string | The name of the replication container. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mappings`](#parameter-replicationcontainersmappings) | array | Replication containers mappings to create. | + +### Parameter: `replicationContainers.name` + +The name of the replication container. + +- Required: Yes +- Type: string + +### Parameter: `replicationContainers.mappings` + +Replication containers mappings to create. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-replicationcontainersmappingsname) | string | The name of the replication container mapping. If not provided, it will be automatically generated as `-`. | +| [`policyName`](#parameter-replicationcontainersmappingspolicyname) | string | Name of the replication policy. Will be ignored if policyResourceId is also specified. | +| [`policyResourceId`](#parameter-replicationcontainersmappingspolicyresourceid) | string | Resource ID of the replication policy. If defined, policyName will be ignored. | +| [`targetContainerFabricName`](#parameter-replicationcontainersmappingstargetcontainerfabricname) | string | Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetContainerName`](#parameter-replicationcontainersmappingstargetcontainername) | string | Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetProtectionContainerResourceId`](#parameter-replicationcontainersmappingstargetprotectioncontainerresourceid) | string | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | + +### Parameter: `replicationContainers.mappings.name` + +The name of the replication container mapping. If not provided, it will be automatically generated as `-`. + +- Required: No +- Type: string + +### Parameter: `replicationContainers.mappings.policyName` + +Name of the replication policy. Will be ignored if policyResourceId is also specified. + +- Required: No +- Type: string + +### Parameter: `replicationContainers.mappings.policyResourceId` + +Resource ID of the replication policy. If defined, policyName will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationContainers.mappings.targetContainerFabricName` + +Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationContainers.mappings.targetContainerName` + +Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `replicationContainers.mappings.targetProtectionContainerResourceId` + +Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. + +- Required: No +- Type: string ## Outputs diff --git a/avm/res/recovery-services/vault/replication-fabric/main.bicep b/avm/res/recovery-services/vault/replication-fabric/main.bicep index 05ee580de4..19f5d6c3bc 100644 --- a/avm/res/recovery-services/vault/replication-fabric/main.bicep +++ b/avm/res/recovery-services/vault/replication-fabric/main.bicep @@ -13,10 +13,15 @@ param location string = resourceGroup().location param name string = location @description('Optional. Replication containers to create.') -param replicationContainers array = [] +param replicationContainers containerType[]? + +resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2024-10-01' existing = { + name: recoveryVaultName +} resource replicationFabric 'Microsoft.RecoveryServices/vaults/replicationFabrics@2022-10-01' = { - name: '${recoveryVaultName}/${name}' + name: name + parent: recoveryServicesVault properties: { customDetails: { instanceType: 'Azure' @@ -26,13 +31,13 @@ resource replicationFabric 'Microsoft.RecoveryServices/vaults/replicationFabrics } module fabric_replicationContainers 'replication-protection-container/main.bicep' = [ - for (container, index) in replicationContainers: { + for (container, index) in (replicationContainers ?? []): { name: '${deployment().name}-RCont-${index}' params: { name: container.name recoveryVaultName: recoveryVaultName replicationFabricName: name - replicationContainerMappings: container.?replicationContainerMappings + mappings: container.?mappings } dependsOn: [ replicationFabric @@ -48,3 +53,18 @@ output resourceId string = replicationFabric.id @description('The name of the resource group the replication fabric was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +import { mappingType } from 'replication-protection-container/main.bicep' +@export() +@description('The type for a replication protection container.') +type containerType = { + @description('Required. The name of the replication container.') + name: string + + @description('Optional. Replication containers mappings to create.') + mappings: mappingType[]? +} diff --git a/avm/res/recovery-services/vault/replication-fabric/main.json b/avm/res/recovery-services/vault/replication-fabric/main.json index fa8d97df0a..8869864c78 100644 --- a/avm/res/recovery-services/vault/replication-fabric/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/main.json @@ -1,15 +1,96 @@ { "$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.32.4.45862", - "templateHash": "10507112348892069298" + "version": "0.33.13.18514", + "templateHash": "8049192542960910465" }, "name": "Recovery Services Vault Replication Fabrics", "description": "This module deploys a Replication Fabric for Azure to Azure disaster recovery scenario of Azure Site Recovery.\n\n> Note: this module currently support only the `instanceType: 'Azure'` scenario." }, + "definitions": { + "containerType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication container." + } + }, + "mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/mappingType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Replication containers mappings to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a replication protection container." + } + }, + "mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "description": "The type for protection container mappings.", + "__bicep_imported_from!": { + "sourceTemplate": "replication-protection-container/main.bicep" + } + } + } + }, "parameters": { "recoveryVaultName": { "type": "string", @@ -33,14 +114,23 @@ }, "replicationContainers": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/containerType" + }, + "nullable": true, "metadata": { "description": "Optional. Replication containers to create." } } }, - "resources": [ - { + "resources": { + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationFabric": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]", @@ -51,10 +141,10 @@ } } }, - { + "fabric_replicationContainers": { "copy": { "name": "fabric_replicationContainers", - "count": "[length(parameters('replicationContainers'))]" + "count": "[length(coalesce(parameters('replicationContainers'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -66,7 +156,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('replicationContainers')[copyIndex()].name]" + "value": "[coalesce(parameters('replicationContainers'), createArray())[copyIndex()].name]" }, "recoveryVaultName": { "value": "[parameters('recoveryVaultName')]" @@ -74,22 +164,76 @@ "replicationFabricName": { "value": "[parameters('name')]" }, - "replicationContainerMappings": { - "value": "[tryGet(parameters('replicationContainers')[copyIndex()], 'replicationContainerMappings')]" + "mappings": { + "value": "[tryGet(coalesce(parameters('replicationContainers'), createArray())[copyIndex()], 'mappings')]" } }, "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.32.4.45862", - "templateHash": "11569119002723688025" + "version": "0.33.13.18514", + "templateHash": "852813454503469785" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." }, + "definitions": { + "mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for protection container mappings." + } + } + }, "parameters": { "recoveryVaultName": { "type": "string", @@ -109,16 +253,31 @@ "description": "Required. The name of the replication container." } }, - "replicationContainerMappings": { + "mappings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/mappingType" + }, + "nullable": true, "metadata": { "description": "Optional. Replication containers mappings to create." } } }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]", @@ -130,10 +289,10 @@ ] } }, - { + "fabric_container_containerMappings": { "copy": { "name": "fabric_container_containerMappings", - "count": "[length(parameters('replicationContainerMappings'))]" + "count": "[length(coalesce(parameters('mappings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -145,13 +304,13 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'name')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'name')]" }, - "policyId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyId')]" + "policyResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyResourceId')]" }, "policyName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyName')]" }, "recoveryVaultName": { "value": "[parameters('recoveryVaultName')]" @@ -162,24 +321,25 @@ "sourceProtectionContainerName": { "value": "[parameters('name')]" }, - "targetProtectionContainerId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetProtectionContainerId')]" + "targetProtectionContainerResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetProtectionContainerResourceId')]" }, "targetContainerFabricName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerFabricName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerFabricName')]" }, "targetContainerName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerName')]" } }, "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.32.4.45862", - "templateHash": "9324067216015127289" + "version": "0.33.13.18514", + "templateHash": "17160081039503517469" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." @@ -203,9 +363,9 @@ "description": "Conditional. The name of the parent source Replication container. Required if the template is used in a standalone deployment." } }, - "targetProtectionContainerId": { + "targetProtectionContainerResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." } @@ -214,17 +374,17 @@ "type": "string", "defaultValue": "[parameters('replicationFabricName')]", "metadata": { - "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, "targetContainerName": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, - "policyId": { + "policyResourceId": { "type": "string", "defaultValue": "", "metadata": { @@ -235,7 +395,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the replication policy. Will be ignored if policyId is also specified." + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." } }, "name": { @@ -247,38 +407,56 @@ } }, "variables": { - "policyResourceId": "[if(not(equals(parameters('policyId'), '')), parameters('policyId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", - "targetProtectionContainerResourceId": "[if(not(equals(parameters('targetProtectionContainerId'), '')), parameters('targetProtectionContainerId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", - "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('targetProtectionContainerResourceId'), '/')[10]))]" + "calcPolicyResourceId": "[if(not(empty(parameters('policyResourceId'))), parameters('policyResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", + "calcTargetProtectionContainerResourceId": "[if(not(empty(parameters('targetProtectionContainerResourceId'))), parameters('targetProtectionContainerResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", + "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('calcTargetProtectionContainerResourceId'), '/')[10]))]" }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric::replicationContainer": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'))]" + }, + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]", "properties": { - "targetProtectionContainerId": "[variables('targetProtectionContainerResourceId')]", - "policyId": "[variables('policyResourceId')]", + "targetProtectionContainerId": "[variables('calcTargetProtectionContainerResourceId')]", + "policyId": "[variables('calcPolicyResourceId')]", "providerSpecificInput": { "instanceType": "A2A" } } } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" + "value": "[variables('mappingName')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[0], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[1], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[2], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[3])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" }, "resourceGroupName": { "type": "string", @@ -291,24 +469,24 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "replicationContainer" ] } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -321,24 +499,24 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1])]" + "replicationFabric" ] } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication fabric." }, - "value": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication fabric." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[0], split(format('{0}/{1}', parameters('recoveryVaultName'), parameters('name')), '/')[1])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics', parameters('recoveryVaultName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/README.md b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/README.md index 76b8606ba1..c747eac6d0 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/README.md +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/README.md @@ -36,7 +36,7 @@ This module deploys a Recovery Services Vault Replication Protection Container. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`replicationContainerMappings`](#parameter-replicationcontainermappings) | array | Replication containers mappings to create. | +| [`mappings`](#parameter-mappings) | array | Replication containers mappings to create. | ### Parameter: `name` @@ -59,13 +59,65 @@ The name of the parent Replication Fabric. Required if the template is used in a - Required: Yes - Type: string -### Parameter: `replicationContainerMappings` +### Parameter: `mappings` Replication containers mappings to create. - Required: No - Type: array -- Default: `[]` + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-mappingsname) | string | The name of the replication container mapping. If not provided, it will be automatically generated as `-`. | +| [`policyName`](#parameter-mappingspolicyname) | string | Name of the replication policy. Will be ignored if policyResourceId is also specified. | +| [`policyResourceId`](#parameter-mappingspolicyresourceid) | string | Resource ID of the replication policy. If defined, policyName will be ignored. | +| [`targetContainerFabricName`](#parameter-mappingstargetcontainerfabricname) | string | Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetContainerName`](#parameter-mappingstargetcontainername) | string | Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetProtectionContainerResourceId`](#parameter-mappingstargetprotectioncontainerresourceid) | string | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | + +### Parameter: `mappings.name` + +The name of the replication container mapping. If not provided, it will be automatically generated as `-`. + +- Required: No +- Type: string + +### Parameter: `mappings.policyName` + +Name of the replication policy. Will be ignored if policyResourceId is also specified. + +- Required: No +- Type: string + +### Parameter: `mappings.policyResourceId` + +Resource ID of the replication policy. If defined, policyName will be ignored. + +- Required: No +- Type: string + +### Parameter: `mappings.targetContainerFabricName` + +Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `mappings.targetContainerName` + +Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. + +- Required: No +- Type: string + +### Parameter: `mappings.targetProtectionContainerResourceId` + +Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. + +- Required: No +- Type: string ## Outputs diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.bicep b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.bicep index fbd8140aca..e9997a3938 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.bicep +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.bicep @@ -13,10 +13,19 @@ param replicationFabricName string param name string @description('Optional. Replication containers mappings to create.') -param replicationContainerMappings array = [] +param mappings mappingType[]? + +resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2024-10-01' existing = { + name: recoveryVaultName + + resource replicationFabric 'replicationFabrics@2022-10-01' existing = { + name: replicationFabricName + } +} resource replicationContainer 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers@2022-10-01' = { - name: '${recoveryVaultName}/${replicationFabricName}/${name}' + name: name + parent: recoveryServicesVault::replicationFabric properties: { providerSpecificInput: [ { @@ -27,22 +36,19 @@ resource replicationContainer 'Microsoft.RecoveryServices/vaults/replicationFabr } module fabric_container_containerMappings 'replication-protection-container-mapping/main.bicep' = [ - for (mapping, index) in replicationContainerMappings: { + for (mapping, index) in (mappings ?? []): { name: '${deployment().name}-Map-${index}' params: { name: mapping.?name - policyId: mapping.?policyId + policyResourceId: mapping.?policyResourceId policyName: mapping.?policyName recoveryVaultName: recoveryVaultName replicationFabricName: replicationFabricName - sourceProtectionContainerName: name - targetProtectionContainerId: mapping.?targetProtectionContainerId + sourceProtectionContainerName: replicationContainer.name + targetProtectionContainerResourceId: mapping.?targetProtectionContainerResourceId targetContainerFabricName: mapping.?targetContainerFabricName targetContainerName: mapping.?targetContainerName } - dependsOn: [ - replicationContainer - ] } ] @@ -54,3 +60,29 @@ output resourceId string = replicationContainer.id @description('The name of the resource group the replication container was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for protection container mappings.') +type mappingType = { + @description('Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored.') + targetProtectionContainerResourceId: string? + + @description('Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored.') + targetContainerFabricName: string? + + @description('Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored.') + targetContainerName: string? + + @description('Optional. Resource ID of the replication policy. If defined, policyName will be ignored.') + policyResourceId: string? + + @description('Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified.') + policyName: string? + + @description('Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`.') + name: string? +} diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json index 574de7d536..cab7041379 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json @@ -1,15 +1,69 @@ { "$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.32.4.45862", - "templateHash": "11569119002723688025" + "version": "0.33.13.18514", + "templateHash": "852813454503469785" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." }, + "definitions": { + "mappingType": { + "type": "object", + "properties": { + "targetProtectionContainerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." + } + }, + "targetContainerFabricName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "targetContainerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." + } + }, + "policyResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the replication policy. If defined, policyName will be ignored." + } + }, + "policyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for protection container mappings." + } + } + }, "parameters": { "recoveryVaultName": { "type": "string", @@ -29,16 +83,31 @@ "description": "Required. The name of the replication container." } }, - "replicationContainerMappings": { + "mappings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/mappingType" + }, + "nullable": true, "metadata": { "description": "Optional. Replication containers mappings to create." } } }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]", @@ -50,10 +119,10 @@ ] } }, - { + "fabric_container_containerMappings": { "copy": { "name": "fabric_container_containerMappings", - "count": "[length(parameters('replicationContainerMappings'))]" + "count": "[length(coalesce(parameters('mappings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -65,13 +134,13 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'name')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'name')]" }, - "policyId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyId')]" + "policyResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyResourceId')]" }, "policyName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'policyName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'policyName')]" }, "recoveryVaultName": { "value": "[parameters('recoveryVaultName')]" @@ -82,24 +151,25 @@ "sourceProtectionContainerName": { "value": "[parameters('name')]" }, - "targetProtectionContainerId": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetProtectionContainerId')]" + "targetProtectionContainerResourceId": { + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetProtectionContainerResourceId')]" }, "targetContainerFabricName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerFabricName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerFabricName')]" }, "targetContainerName": { - "value": "[tryGet(parameters('replicationContainerMappings')[copyIndex()], 'targetContainerName')]" + "value": "[tryGet(coalesce(parameters('mappings'), createArray())[copyIndex()], 'targetContainerName')]" } }, "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.32.4.45862", - "templateHash": "9324067216015127289" + "version": "0.33.13.18514", + "templateHash": "17160081039503517469" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." @@ -123,9 +193,9 @@ "description": "Conditional. The name of the parent source Replication container. Required if the template is used in a standalone deployment." } }, - "targetProtectionContainerId": { + "targetProtectionContainerResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." } @@ -134,17 +204,17 @@ "type": "string", "defaultValue": "[parameters('replicationFabricName')]", "metadata": { - "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, "targetContainerName": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, - "policyId": { + "policyResourceId": { "type": "string", "defaultValue": "", "metadata": { @@ -155,7 +225,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the replication policy. Will be ignored if policyId is also specified." + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." } }, "name": { @@ -167,38 +237,56 @@ } }, "variables": { - "policyResourceId": "[if(not(equals(parameters('policyId'), '')), parameters('policyId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", - "targetProtectionContainerResourceId": "[if(not(equals(parameters('targetProtectionContainerId'), '')), parameters('targetProtectionContainerId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", - "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('targetProtectionContainerResourceId'), '/')[10]))]" + "calcPolicyResourceId": "[if(not(empty(parameters('policyResourceId'))), parameters('policyResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", + "calcTargetProtectionContainerResourceId": "[if(not(empty(parameters('targetProtectionContainerResourceId'))), parameters('targetProtectionContainerResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", + "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('calcTargetProtectionContainerResourceId'), '/')[10]))]" }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric::replicationContainer": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'))]" + }, + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]", "properties": { - "targetProtectionContainerId": "[variables('targetProtectionContainerResourceId')]", - "policyId": "[variables('policyResourceId')]", + "targetProtectionContainerId": "[variables('calcTargetProtectionContainerResourceId')]", + "policyId": "[variables('calcPolicyResourceId')]", "providerSpecificInput": { "instanceType": "A2A" } } } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" + "value": "[variables('mappingName')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[0], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[1], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[2], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[3])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" }, "resourceGroupName": { "type": "string", @@ -211,24 +299,24 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "replicationContainer" ] } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[0], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[1], split(format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name')), '/')[2])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/README.md b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/README.md index 59d9063217..c4557fa801 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/README.md +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/README.md @@ -31,11 +31,11 @@ This module deploys a Recovery Services Vault (RSV) Replication Protection Conta | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the replication container mapping. If not provided, it will be automatically generated as `-`. | -| [`policyId`](#parameter-policyid) | string | Resource ID of the replication policy. If defined, policyName will be ignored. | -| [`policyName`](#parameter-policyname) | string | Name of the replication policy. Will be ignored if policyId is also specified. | -| [`targetContainerFabricName`](#parameter-targetcontainerfabricname) | string | Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored. | -| [`targetContainerName`](#parameter-targetcontainername) | string | Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored. | -| [`targetProtectionContainerId`](#parameter-targetprotectioncontainerid) | string | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | +| [`policyName`](#parameter-policyname) | string | Name of the replication policy. Will be ignored if policyResourceId is also specified. | +| [`policyResourceId`](#parameter-policyresourceid) | string | Resource ID of the replication policy. If defined, policyName will be ignored. | +| [`targetContainerFabricName`](#parameter-targetcontainerfabricname) | string | Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetContainerName`](#parameter-targetcontainername) | string | Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. | +| [`targetProtectionContainerResourceId`](#parameter-targetprotectioncontainerresourceid) | string | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | ### Parameter: `recoveryVaultName` @@ -66,17 +66,17 @@ The name of the replication container mapping. If not provided, it will be autom - Type: string - Default: `''` -### Parameter: `policyId` +### Parameter: `policyName` -Resource ID of the replication policy. If defined, policyName will be ignored. +Name of the replication policy. Will be ignored if policyResourceId is also specified. - Required: No - Type: string - Default: `''` -### Parameter: `policyName` +### Parameter: `policyResourceId` -Name of the replication policy. Will be ignored if policyId is also specified. +Resource ID of the replication policy. If defined, policyName will be ignored. - Required: No - Type: string @@ -84,7 +84,7 @@ Name of the replication policy. Will be ignored if policyId is also specified. ### Parameter: `targetContainerFabricName` -Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored. +Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored. - Required: No - Type: string @@ -92,19 +92,18 @@ Name of the fabric containing the target container. If targetProtectionContainer ### Parameter: `targetContainerName` -Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored. +Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored. - Required: No - Type: string - Default: `''` -### Parameter: `targetProtectionContainerId` +### Parameter: `targetProtectionContainerResourceId` Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. - Required: No - Type: string -- Default: `''` ## Outputs diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.bicep b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.bicep index 98b9ef47d1..19799272dd 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.bicep +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.bicep @@ -13,28 +13,28 @@ param replicationFabricName string param sourceProtectionContainerName string @description('Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored.') -param targetProtectionContainerId string = '' +param targetProtectionContainerResourceId string? -@description('Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored.') +@description('Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored.') param targetContainerFabricName string = replicationFabricName -@description('Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored.') +@description('Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored.') param targetContainerName string = '' @description('Optional. Resource ID of the replication policy. If defined, policyName will be ignored.') -param policyId string = '' +param policyResourceId string = '' -@description('Optional. Name of the replication policy. Will be ignored if policyId is also specified.') +@description('Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified.') param policyName string = '' @description('Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`.') param name string = '' -var policyResourceId = policyId != '' - ? policyId +var calcPolicyResourceId = !empty(policyResourceId) + ? policyResourceId : subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', recoveryVaultName, policyName) -var targetProtectionContainerResourceId = targetProtectionContainerId != '' - ? targetProtectionContainerId +var calcTargetProtectionContainerResourceId = !empty(targetProtectionContainerResourceId) + ? targetProtectionContainerResourceId : subscriptionResourceId( 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', recoveryVaultName, @@ -43,13 +43,26 @@ var targetProtectionContainerResourceId = targetProtectionContainerId != '' ) var mappingName = !empty(name) ? name - : '${sourceProtectionContainerName}-${split(targetProtectionContainerResourceId, '/')[10]}' + : '${sourceProtectionContainerName}-${split(calcTargetProtectionContainerResourceId!, '/')[10]}' + +resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2024-10-01' existing = { + name: recoveryVaultName + + resource replicationFabric 'replicationFabrics@2022-10-01' existing = { + name: replicationFabricName + + resource replicationContainer 'replicationProtectionContainers@2022-10-01' existing = { + name: sourceProtectionContainerName + } + } +} resource replicationContainer 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings@2022-10-01' = { - name: '${recoveryVaultName}/${replicationFabricName}/${sourceProtectionContainerName}/${mappingName}' + name: mappingName + parent: recoveryServicesVault::replicationFabric::replicationContainer properties: { - targetProtectionContainerId: targetProtectionContainerResourceId - policyId: policyResourceId + targetProtectionContainerId: calcTargetProtectionContainerResourceId + policyId: calcPolicyResourceId providerSpecificInput: { instanceType: 'A2A' } diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json index b1dc3b51dc..4e34240f55 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json @@ -1,11 +1,12 @@ { "$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.32.4.45862", - "templateHash": "9324067216015127289" + "version": "0.33.13.18514", + "templateHash": "17160081039503517469" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." @@ -29,9 +30,9 @@ "description": "Conditional. The name of the parent source Replication container. Required if the template is used in a standalone deployment." } }, - "targetProtectionContainerId": { + "targetProtectionContainerResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored." } @@ -40,17 +41,17 @@ "type": "string", "defaultValue": "[parameters('replicationFabricName')]", "metadata": { - "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the fabric containing the target container. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, "targetContainerName": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored." + "description": "Optional. Name of the target container. Must be specified if targetProtectionContainerResourceId is not. If targetProtectionContainerResourceId is specified, this parameter will be ignored." } }, - "policyId": { + "policyResourceId": { "type": "string", "defaultValue": "", "metadata": { @@ -61,7 +62,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. Name of the replication policy. Will be ignored if policyId is also specified." + "description": "Optional. Name of the replication policy. Will be ignored if policyResourceId is also specified." } }, "name": { @@ -73,38 +74,56 @@ } }, "variables": { - "policyResourceId": "[if(not(equals(parameters('policyId'), '')), parameters('policyId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", - "targetProtectionContainerResourceId": "[if(not(equals(parameters('targetProtectionContainerId'), '')), parameters('targetProtectionContainerId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", - "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('targetProtectionContainerResourceId'), '/')[10]))]" + "calcPolicyResourceId": "[if(not(empty(parameters('policyResourceId'))), parameters('policyResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', parameters('recoveryVaultName'), parameters('policyName')))]", + "calcTargetProtectionContainerResourceId": "[if(not(empty(parameters('targetProtectionContainerResourceId'))), parameters('targetProtectionContainerResourceId'), subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', parameters('recoveryVaultName'), parameters('targetContainerFabricName'), parameters('targetContainerName')))]", + "mappingName": "[if(not(empty(parameters('name'))), parameters('name'), format('{0}-{1}', parameters('sourceProtectionContainerName'), split(variables('calcTargetProtectionContainerResourceId'), '/')[10]))]" }, - "resources": [ - { + "resources": { + "recoveryServicesVault::replicationFabric::replicationContainer": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}/{2}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'))]" + }, + "recoveryServicesVault::replicationFabric": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults/replicationFabrics", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('recoveryVaultName'), parameters('replicationFabricName'))]" + }, + "recoveryServicesVault": { + "existing": true, + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2024-10-01", + "name": "[parameters('recoveryVaultName')]" + }, + "replicationContainer": { "type": "Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings", "apiVersion": "2022-10-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]", "properties": { - "targetProtectionContainerId": "[variables('targetProtectionContainerResourceId')]", - "policyId": "[variables('policyResourceId')]", + "targetProtectionContainerId": "[variables('calcTargetProtectionContainerResourceId')]", + "policyId": "[variables('calcPolicyResourceId')]", "providerSpecificInput": { "instanceType": "A2A" } } } - ], + }, "outputs": { "name": { "type": "string", "metadata": { "description": "The name of the replication container." }, - "value": "[format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" + "value": "[variables('mappingName')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the replication container." }, - "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[0], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[1], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[2], split(format('{0}/{1}/{2}/{3}', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName')), '/')[3])]" + "value": "[resourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings', parameters('recoveryVaultName'), parameters('replicationFabricName'), parameters('sourceProtectionContainerName'), variables('mappingName'))]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/recovery-services/vault/replication-policy/main.json b/avm/res/recovery-services/vault/replication-policy/main.json index 5545634660..8686c9013f 100644 --- a/avm/res/recovery-services/vault/replication-policy/main.json +++ b/avm/res/recovery-services/vault/replication-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3181787105854140022" + "version": "0.33.13.18514", + "templateHash": "10948839526456404680" }, "name": "Recovery Services Vault Replication Policies", "description": "This module deploys a Recovery Services Vault Replication Policy for Disaster Recovery scenario.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario." diff --git a/avm/res/recovery-services/vault/tests/e2e/dr/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/dr/main.test.bicep index 1e88c6427f..d9df105e54 100644 --- a/avm/res/recovery-services/vault/tests/e2e/dr/main.test.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/dr/main.test.bicep @@ -49,17 +49,17 @@ module testDeployment '../../../main.bicep' = [ replicationContainers: [ { name: 'ne-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerName: 'pluto' - targetProtectionContainerId: '${resourceGroup.id}/providers/Microsoft.RecoveryServices/vaults/${rsvName}/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container2' + targetProtectionContainerResourceId: '${resourceGroup.id}/providers/Microsoft.RecoveryServices/vaults/${rsvName}/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container2' } ] } { name: 'ne-container2' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'WE-2' @@ -75,7 +75,7 @@ module testDeployment '../../../main.bicep' = [ replicationContainers: [ { name: 'we-container1' - replicationContainerMappings: [ + mappings: [ { policyName: 'Default_values' targetContainerFabricName: 'NorthEurope' diff --git a/avm/res/recovery-services/vault/tests/e2e/encr/dependencies.bicep b/avm/res/recovery-services/vault/tests/e2e/encr/dependencies.bicep new file mode 100644 index 0000000000..859e78741b --- /dev/null +++ b/avm/res/recovery-services/vault/tests/e2e/encr/dependencies.bicep @@ -0,0 +1,61 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@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 + +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 for encryption to work + 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-Reader-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 Key Vault Encryption Key.') +output keyVaultEncryptionKeyName string = keyVault::key.name + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/avm/res/recovery-services/vault/tests/e2e/encr/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/encr/main.test.bicep new file mode 100644 index 0000000000..127122d8ec --- /dev/null +++ b/avm/res/recovery-services/vault/tests/e2e/encr/main.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +metadata name = 'Using encryption with Customer-Managed-Key' +metadata description = 'This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-automation.account-${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 = 'rsvencr' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@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: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // 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)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + 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' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + autoRotationEnabled: false + } + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + location: resourceLocation + } + } +] diff --git a/avm/res/recovery-services/vault/tests/e2e/max/dependencies.bicep b/avm/res/recovery-services/vault/tests/e2e/max/dependencies.bicep index 12b8653f54..a561567f44 100644 --- a/avm/res/recovery-services/vault/tests/e2e/max/dependencies.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/max/dependencies.bicep @@ -7,6 +7,15 @@ param virtualNetworkName string @description('Required. The name of the Managed Identity to create.') param managedIdentityName string +@description('Required. The name of the Deployment Script to create for the SSH Key generation.') +param sshDeploymentScriptName string + +@description('Required. The name of the SSH Key to create.') +param sshKeyName string + +@description('Required. The name of the virtual machine to create.') +param virtualMachineName string + var addressPrefix = '10.0.0.0/16' resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { @@ -29,6 +38,99 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } } +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource msiRGContrRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, 'Contributor', managedIdentity.id) + scope: resourceGroup() + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'b24988ac-6180-42a0-ab88-20f7382dd24c' + ) // Contributor + principalType: 'ServicePrincipal' + } +} + +resource sshDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: sshDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '9.0' + retentionInterval: 'P1D' + arguments: '-SSHKeyName "${sshKeyName}" -ResourceGroupName "${resourceGroup().name}"' + scriptContent: loadTextContent('../../../../../../../utilities/e2e-template-assets/scripts/New-SSHKey.ps1') + } + dependsOn: [ + msiRGContrRoleAssignment + ] +} + +resource sshKey 'Microsoft.Compute/sshPublicKeys@2022-03-01' = { + name: sshKeyName + location: location + properties: { + publicKey: sshDeploymentScript.properties.outputs.publicKey + } +} + +module vm 'br/public:avm/res/compute/virtual-machine:0.10.1' = { + name: '${uniqueString(deployment().name, location)}-virtualMachine' + params: { + location: location + name: virtualMachineName + adminUsername: 'localAdminUser' + imageReference: { + publisher: 'Canonical' + offer: '0001-com-ubuntu-server-jammy' + sku: '22_04-lts-gen2' + version: 'latest' + } + zone: 0 + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: virtualNetwork.properties.subnets[0].id + pipConfiguration: { + name: 'pip-01' + } + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: 128 + caching: 'ReadWrite' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + vmSize: 'Standard_D2s_v3' + disablePasswordAuthentication: true + publicKeys: [ + { + keyData: sshKey.properties.publicKey + path: '/home/localAdminUser/.ssh/authorized_keys' + } + ] + } +} + resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { name: 'privatelink.siterecovery.windowsazure.com' location: 'global' @@ -45,11 +147,6 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { } } -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 @@ -61,3 +158,6 @@ output managedIdentityResourceId string = managedIdentity.id @description('The resource ID of the created Private DNS Zone.') output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Virtual Machine.') +output virtualMachineResourceId string = vm.outputs.resourceId diff --git a/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep index 27c43a282f..177bc345cb 100644 --- a/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep @@ -38,6 +38,9 @@ module nestedDependencies 'dependencies.bicep' = { location: resourceLocation virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + sshDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}' + virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}' } } @@ -71,6 +74,15 @@ module testDeployment '../../../main.bicep' = [ enhancedSecurityState: 'Disabled' softDeleteFeatureState: 'Disabled' } + protectedItems: [ + { + name: 'vm;iaasvmcontainerv2;${resourceGroup.name};${last(split(nestedDependencies.outputs.virtualMachineResourceId, '/'))}' + protectionContainerName: 'IaasVMContainer;iaasvmcontainerv2;${resourceGroup.name};${last(split(nestedDependencies.outputs.virtualMachineResourceId, '/'))}' + policyName: 'VMpolicy' + protectedItemType: 'Microsoft.Compute/virtualMachines' + sourceResourceId: nestedDependencies.outputs.virtualMachineResourceId + } + ] backupPolicies: [ { name: 'VMpolicy' @@ -301,10 +313,6 @@ module testDeployment '../../../main.bicep' = [ } } ] - backupStorageConfig: { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' - } replicationAlertSettings: { customEmailAddresses: [ 'test.user@testcompany.com' @@ -420,9 +428,12 @@ module testDeployment '../../../main.bicep' = [ monitoringSettings: { azureMonitorAlertSettings: { alertsForAllJobFailures: 'Enabled' + alertsForAllFailoverIssues: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } securitySettings: { diff --git a/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep index 6311e84bed..54126852cf 100644 --- a/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep @@ -301,10 +301,6 @@ module testDeployment '../../../main.bicep' = [ } } ] - backupStorageConfig: { - crossRegionRestoreFlag: true - storageModelType: 'GeoRedundant' - } replicationAlertSettings: { customEmailAddresses: [ 'test.user@testcompany.com' @@ -398,9 +394,12 @@ module testDeployment '../../../main.bicep' = [ monitoringSettings: { azureMonitorAlertSettings: { alertsForAllJobFailures: 'Enabled' + alertsForAllFailoverIssues: 'Enabled' + alertsForAllReplicationIssues: 'Enabled' } classicAlertSettings: { alertsForCriticalOperations: 'Enabled' + emailNotificationsForSiteRecovery: 'Enabled' } } securitySettings: { diff --git a/avm/res/recovery-services/vault/version.json b/avm/res/recovery-services/vault/version.json index 21226dd43f..09c3664cec 100644 --- a/avm/res/recovery-services/vault/version.json +++ b/avm/res/recovery-services/vault/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] From 03f140c77dc8cd868597667d477c4526b9cab848 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 11 Feb 2025 09:32:31 +0100 Subject: [PATCH 14/53] fix: Fixed PE deployment when defining a different RG/Subscription than the default (#4421) ## Description Validated scenarios - Use default (i.e., subnet): works - Specify dedicated RG resource Id: works ## Pipeline Reference | Pipeline | | -------- | [![avm.res.cache.redis](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) [![avm.res.container-registry.registry](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml) [![avm.res.databricks.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) (unrelated) [![avm.res.db-for-postgre-sql.flexible-server](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml) [![avm.res.key-vault.vault](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) [![avm.res.purview.account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml) [![avm.res.recovery-services.vault](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) [![avm.res.relay.namespace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml) [![avm.res.service-bus.namespace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) [![avm.res.sql.server](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) [![avm.res.storage.storage-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) [![avm.res.synapse.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml) [![avm.res.web.site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) [![avm.res.web.static-site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml/badge.svg?branch=users%2Falsehr%2FkvltPERGTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml) ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --- avm/res/cache/redis/main.bicep | 13 +-- avm/res/cache/redis/main.json | 4 +- .../container-registry/registry/main.bicep | 13 +-- avm/res/container-registry/registry/main.json | 26 ++--- avm/res/databricks/workspace/main.bicep | 26 ++--- avm/res/databricks/workspace/main.json | 8 +- .../flexible-server/main.bicep | 13 +-- .../flexible-server/main.json | 4 +- avm/res/key-vault/vault/README.md | 5 +- .../key-vault/vault/access-policy/main.json | 4 +- avm/res/key-vault/vault/key/main.json | 4 +- avm/res/key-vault/vault/main.bicep | 20 ++-- avm/res/key-vault/vault/main.json | 102 +++++------------- avm/res/key-vault/vault/secret/main.json | 4 +- .../vault/tests/e2e/max/main.test.bicep | 1 + avm/res/purview/account/main.bicep | 65 ++++------- avm/res/purview/account/main.json | 12 ++- avm/res/recovery-services/vault/main.bicep | 29 +++-- avm/res/recovery-services/vault/main.json | 12 ++- avm/res/relay/namespace/main.bicep | 13 +-- avm/res/relay/namespace/main.json | 4 +- avm/res/service-bus/namespace/main.bicep | 13 +-- avm/res/service-bus/namespace/main.json | 46 ++++---- avm/res/sql/server/main.bicep | 13 +-- avm/res/sql/server/main.json | 4 +- avm/res/storage/storage-account/main.bicep | 13 +-- avm/res/storage/storage-account/main.json | 54 +++++----- avm/res/synapse/workspace/main.bicep | 13 +-- avm/res/synapse/workspace/main.json | 26 ++--- avm/res/web/site/main.bicep | 13 +-- avm/res/web/site/main.json | 60 ++++++----- avm/res/web/site/slot/main.bicep | 13 +-- avm/res/web/site/slot/main.json | 26 ++--- avm/res/web/static-site/main.bicep | 13 +-- avm/res/web/static-site/main.json | 22 ++-- 35 files changed, 303 insertions(+), 408 deletions(-) diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index c8b5c6167d..461bab33b7 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -304,15 +304,10 @@ resource redis_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04- module redis_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-redis-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(redis.id, '/'))}-${privateEndpoint.?service ?? 'redisCache'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index 7697018b60..10bffbc878 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "17774807916018865720" + "templateHash": "1242857667100916577" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache." @@ -1167,6 +1167,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-redis-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/container-registry/registry/main.bicep b/avm/res/container-registry/registry/main.bicep index 7b7670fd32..f6a01e5d55 100644 --- a/avm/res/container-registry/registry/main.bicep +++ b/avm/res/container-registry/registry/main.bicep @@ -447,15 +447,10 @@ resource registry_roleAssignments 'Microsoft.Authorization/roleAssignments@2022- module registry_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-registry-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(registry.id, '/'))}-${privateEndpoint.?service ?? 'registry'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json index a125c7727e..c28fcccf4d 100644 --- a/avm/res/container-registry/registry/main.json +++ b/avm/res/container-registry/registry/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7811628046063994725" + "version": "0.33.13.18514", + "templateHash": "1879570214296822193" }, "name": "Azure Container Registries (ACR)", "description": "This module deploys an Azure Container Registry (ACR)." @@ -1415,8 +1415,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "11112300500664950599" + "version": "0.33.13.18514", + "templateHash": "8957375042269792339" }, "name": "Container Registries scopeMaps", "description": "This module deploys an Azure Container Registry (ACR) scopeMap." @@ -1538,8 +1538,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6036875058945996178" + "version": "0.33.13.18514", + "templateHash": "2771208879484692364" }, "name": "Azure Container Registry (ACR) Replications", "description": "This module deploys an Azure Container Registry (ACR) Replication." @@ -1682,8 +1682,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "15848218260506856293" + "version": "0.33.13.18514", + "templateHash": "16839288679507454258" }, "name": "Container Registries Credential Sets", "description": "This module deploys an ACR Credential Set." @@ -1866,8 +1866,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3783697279882479947" + "version": "0.33.13.18514", + "templateHash": "13450234979206794925" }, "name": "Container Registries Cache", "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache))." @@ -2004,8 +2004,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10084997815751263562" + "version": "0.33.13.18514", + "templateHash": "14557981877826360902" }, "name": "Azure Container Registry (ACR) Webhooks", "description": "This module deploys an Azure Container Registry (ACR) Webhook." @@ -2175,6 +2175,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index b75702d2f7..fb1eadf689 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -438,15 +438,10 @@ resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -507,15 +502,10 @@ var _storageAccountId = resourceId( module storageAccount_storageAccountPrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (storageAccountPrivateEndpoints ?? []): if (privateStorageAccount == 'Enabled') { name: '${uniqueString(deployment().name, location)}-workspacestorage-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${_storageAccountName}-${privateEndpoint.service}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index fc365caa58..01d52c2168 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "5589679396689290907" + "templateHash": "5273652821148246861" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace." @@ -1011,8 +1011,8 @@ }, "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), equals(true(), true())), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", "dependsOn": [ - "cMKManagedDiskKeyVault::cMKKey", "cMKKeyVault::cMKKey", + "cMKManagedDiskKeyVault::cMKKey", "cMKKeyVault", "cMKManagedDiskKeyVault" ] @@ -1095,6 +1095,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1853,6 +1855,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspacestorage-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.bicep b/avm/res/db-for-postgre-sql/flexible-server/main.bicep index 08a780886b..dc508d19a4 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/main.bicep @@ -453,15 +453,10 @@ resource flexibleServer_diagnosticSettings 'Microsoft.Insights/diagnosticSetting module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): if (empty(delegatedSubnetResourceId)) { name: '${uniqueString(deployment().name, location)}-PostgreSQL-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(flexibleServer.id, '/'))}-${privateEndpoint.?service ?? 'postgresqlServer'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.json b/avm/res/db-for-postgre-sql/flexible-server/main.json index 111ad605cf..6f63a1e86e 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "8102121291373013517" + "templateHash": "17738367672833372176" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server." @@ -1731,6 +1731,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PostgreSQL-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/key-vault/vault/README.md b/avm/res/key-vault/vault/README.md index 2b076102a2..4fe0b74122 100644 --- a/avm/res/key-vault/vault/README.md +++ b/avm/res/key-vault/vault/README.md @@ -463,6 +463,7 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } ] } + resourceGroupResourceId: '' subnetResourceId: '' } ] @@ -731,6 +732,7 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } ] }, + "resourceGroupResourceId": "", "subnetResourceId": "" } ] @@ -989,6 +991,7 @@ param privateEndpoints = [ } ] } + resourceGroupResourceId: '' subnetResourceId: '' } ] @@ -3168,7 +3171,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.9.0` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/key-vault/vault/access-policy/main.json b/avm/res/key-vault/vault/access-policy/main.json index 74ccbb8e5b..1b7d43a370 100644 --- a/avm/res/key-vault/vault/access-policy/main.json +++ b/avm/res/key-vault/vault/access-policy/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6744070574147884656" + "version": "0.33.13.18514", + "templateHash": "15662374115599475123" }, "name": "Key Vault Access Policies", "description": "This module deploys a Key Vault Access Policy." diff --git a/avm/res/key-vault/vault/key/main.json b/avm/res/key-vault/vault/key/main.json index 562c4cda7c..ecd8d6c830 100644 --- a/avm/res/key-vault/vault/key/main.json +++ b/avm/res/key-vault/vault/key/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15884853996194439605" + "version": "0.33.13.18514", + "templateHash": "8627699258587559559" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key." diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep index 654bcab7d0..3c0200eca1 100644 --- a/avm/res/key-vault/vault/main.bicep +++ b/avm/res/key-vault/vault/main.bicep @@ -304,18 +304,13 @@ module keyVault_keys 'key/main.bicep' = [ } ] -module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.9.0' = [ +module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-keyVault-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(keyVault.id, '/'))}-${privateEndpoint.?service ?? 'vault'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -403,8 +398,8 @@ output privateEndpoints privateEndpointOutputType[] = [ for (item, index) in (privateEndpoints ?? []): { name: keyVault_privateEndpoints[index].outputs.name resourceId: keyVault_privateEndpoints[index].outputs.resourceId - groupId: keyVault_privateEndpoints[index].outputs.groupId - customDnsConfigs: keyVault_privateEndpoints[index].outputs.customDnsConfig + groupId: keyVault_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: keyVault_privateEndpoints[index].outputs.customDnsConfigs networkInterfaceResourceIds: keyVault_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] @@ -432,7 +427,6 @@ output keys credentialOutputType[] = [ // Definitions // // ================ // @export() -@description('The type for a private endpoint output.') type privateEndpointOutputType = { @description('The name of the private endpoint.') name: string diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json index 9bce3471d7..451db99b7b 100644 --- a/avm/res/key-vault/vault/main.json +++ b/avm/res/key-vault/vault/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5900566328584197217" + "version": "0.33.13.18514", + "templateHash": "10562288197267069295" }, "name": "Key Vaults", "description": "This module deploys a Key Vault." @@ -72,8 +72,7 @@ } }, "metadata": { - "__bicep_export!": true, - "description": "The type for a private endpoint output." + "__bicep_export!": true } }, "credentialOutputType": { @@ -1385,8 +1384,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6744070574147884656" + "version": "0.33.13.18514", + "templateHash": "15662374115599475123" }, "name": "Key Vault Access Policies", "description": "This module deploys a Key Vault Access Policy." @@ -1651,8 +1650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6967022677055315423" + "version": "0.33.13.18514", + "templateHash": "7030946530357291103" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret." @@ -1965,8 +1964,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15884853996194439605" + "version": "0.33.13.18514", + "templateHash": "8627699258587559559" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key." @@ -2273,6 +2272,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-keyVault-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2325,12 +2326,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6724714132049298262" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -2397,50 +2397,6 @@ "__bicep_export!": true } }, - "manualPrivateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, "privateLinkServiceConnectionType": { "type": "object", "properties": { @@ -2535,7 +2491,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -2633,7 +2589,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -2729,11 +2685,11 @@ "manualPrivateLinkServiceConnections": { "type": "array", "items": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionType" + "$ref": "#/definitions/privateLinkServiceConnectionType" }, "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { @@ -2743,7 +2699,7 @@ }, "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -2780,7 +2736,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.9.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2886,12 +2842,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12329174801198479603" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -2969,10 +2924,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -3034,7 +2986,7 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { + "customDnsConfigs": { "type": "array", "items": { "$ref": "#/definitions/customDnsConfigType" @@ -3119,8 +3071,8 @@ "input": { "name": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfigs": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "groupId": "[tryGet(tryGet(reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", "networkInterfaceResourceIds": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } diff --git a/avm/res/key-vault/vault/secret/main.json b/avm/res/key-vault/vault/secret/main.json index 6cdad0ef47..75557e043c 100644 --- a/avm/res/key-vault/vault/secret/main.json +++ b/avm/res/key-vault/vault/secret/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6967022677055315423" + "version": "0.33.13.18514", + "templateHash": "7030946530357291103" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret." diff --git a/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep index daeb6d673f..896844e9de 100644 --- a/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep +++ b/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep @@ -249,6 +249,7 @@ module testDeployment '../../../main.bicep' = [ } ] } + resourceGroupResourceId: resourceGroup.id subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] diff --git a/avm/res/purview/account/main.bicep b/avm/res/purview/account/main.bicep index 6821e5bcda..678cd98182 100644 --- a/avm/res/purview/account/main.bicep +++ b/avm/res/purview/account/main.bicep @@ -190,15 +190,10 @@ resource account_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021- module account_accountPrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (accountPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-account-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -250,15 +245,10 @@ module account_accountPrivateEndpoints 'br/public:avm/res/network/private-endpoi module account_portalPrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (portalPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-portal-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'portal'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -310,15 +300,10 @@ module account_portalPrivateEndpoints 'br/public:avm/res/network/private-endpoin module account_storageBlobPrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (storageBlobPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-blob-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'blob'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -370,15 +355,10 @@ module account_storageBlobPrivateEndpoints 'br/public:avm/res/network/private-en module account_storageQueuePrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (storageQueuePrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-queue-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'queue'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -430,15 +410,10 @@ module account_storageQueuePrivateEndpoints 'br/public:avm/res/network/private-e module account_eventHubPrivateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (eventHubPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-eventHub-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/purview/account/main.json b/avm/res/purview/account/main.json index b06c5e1fc1..886766ba2f 100644 --- a/avm/res/purview/account/main.json +++ b/avm/res/purview/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "12517935064774862493" + "templateHash": "17913522692754850617" }, "name": "Purview Accounts", "description": "This module deploys a Purview Account." @@ -875,6 +875,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-account-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1630,6 +1632,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-portal-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2385,6 +2389,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-blob-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3140,6 +3146,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-queue-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3895,6 +3903,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-eventHub-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 96e98b1f01..888cf7054c 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -194,15 +194,15 @@ resource rsv 'Microsoft.RecoveryServices/vaults@2024-04-01' = { ? { azureMonitorAlertSettings: !empty(monitoringSettings.?azureMonitorAlertSettings) ? { - alertsForAllFailoverIssues: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllFailoverIssues ?? 'Enabled' - alertsForAllJobFailures: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllJobFailures ?? 'Enabled' - alertsForAllReplicationIssues: monitoringSettings!.azureMonitorAlertSettings.?alertsForAllReplicationIssues ?? 'Enabled' + alertsForAllFailoverIssues: monitoringSettings!.?azureMonitorAlertSettings.?alertsForAllFailoverIssues ?? 'Enabled' + alertsForAllJobFailures: monitoringSettings!.?azureMonitorAlertSettings.?alertsForAllJobFailures ?? 'Enabled' + alertsForAllReplicationIssues: monitoringSettings!.?azureMonitorAlertSettings.?alertsForAllReplicationIssues ?? 'Enabled' } : null classicAlertSettings: !empty(monitoringSettings.?classicAlertSettings) ? { - alertsForCriticalOperations: monitoringSettings!.classicAlertSettings.?alertsForCriticalOperations ?? 'Enabled' - emailNotificationsForSiteRecovery: monitoringSettings!.classicAlertSettings.?emailNotificationsForSiteRecovery ?? 'Enabled' + alertsForCriticalOperations: monitoringSettings!.?classicAlertSettings.?alertsForCriticalOperations ?? 'Enabled' + emailNotificationsForSiteRecovery: monitoringSettings!.?classicAlertSettings.?emailNotificationsForSiteRecovery ?? 'Enabled' } : null } @@ -223,7 +223,7 @@ resource rsv 'Microsoft.RecoveryServices/vaults@2024-04-01' = { } keyVaultProperties: { keyUri: !empty(customerManagedKey.?keyVersion) - ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' + ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.?keyVersion}' : (customerManagedKey.?autoRotationEnabled ?? true) ? cMKKeyVault::cMKKey.properties.keyUri : cMKKeyVault::cMKKey.properties.keyUriWithVersion @@ -266,8 +266,8 @@ module rsv_backupStorageConfiguration 'backup-storage-config/main.bicep' = if (! name: '${uniqueString(deployment().name, location)}-RSV-BackupStorageConfig' params: { recoveryVaultName: rsv.name - storageModelType: backupStorageConfig!.storageModelType - crossRegionRestoreFlag: backupStorageConfig!.crossRegionRestoreFlag + storageModelType: backupStorageConfig!.?storageModelType + crossRegionRestoreFlag: backupStorageConfig!.?crossRegionRestoreFlag } } @@ -369,15 +369,10 @@ resource rsv_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-0 module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-rsv-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(rsv.id, '/'))}-${privateEndpoint.?service ?? 'AzureSiteRecovery'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index c162dc8dab..189515ddce 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "1404292748211985740" + "templateHash": "16908039055526294885" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault." @@ -1496,12 +1496,12 @@ "tier": "Standard" }, "properties": { - "monitoringSettings": "[if(not(empty(parameters('monitoringSettings'))), createObject('azureMonitorAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'))), createObject('alertsForAllFailoverIssues', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllFailoverIssues'), 'Enabled'), 'alertsForAllJobFailures', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllJobFailures'), 'Enabled'), 'alertsForAllReplicationIssues', coalesce(tryGet(parameters('monitoringSettings').azureMonitorAlertSettings, 'alertsForAllReplicationIssues'), 'Enabled')), null()), 'classicAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'classicAlertSettings'))), createObject('alertsForCriticalOperations', coalesce(tryGet(parameters('monitoringSettings').classicAlertSettings, 'alertsForCriticalOperations'), 'Enabled'), 'emailNotificationsForSiteRecovery', coalesce(tryGet(parameters('monitoringSettings').classicAlertSettings, 'emailNotificationsForSiteRecovery'), 'Enabled')), null())), null())]", + "monitoringSettings": "[if(not(empty(parameters('monitoringSettings'))), createObject('azureMonitorAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'))), createObject('alertsForAllFailoverIssues', coalesce(tryGet(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'), 'alertsForAllFailoverIssues'), 'Enabled'), 'alertsForAllJobFailures', coalesce(tryGet(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'), 'alertsForAllJobFailures'), 'Enabled'), 'alertsForAllReplicationIssues', coalesce(tryGet(tryGet(parameters('monitoringSettings'), 'azureMonitorAlertSettings'), 'alertsForAllReplicationIssues'), 'Enabled')), null()), 'classicAlertSettings', if(not(empty(tryGet(parameters('monitoringSettings'), 'classicAlertSettings'))), createObject('alertsForCriticalOperations', coalesce(tryGet(tryGet(parameters('monitoringSettings'), 'classicAlertSettings'), 'alertsForCriticalOperations'), 'Enabled'), 'emailNotificationsForSiteRecovery', coalesce(tryGet(tryGet(parameters('monitoringSettings'), 'classicAlertSettings'), 'emailNotificationsForSiteRecovery'), 'Enabled')), null())), null())]", "securitySettings": "[parameters('securitySettings')]", "publicNetworkAccess": "[parameters('publicNetworkAccess')]", "redundancySettings": "[parameters('redundancySettings')]", "restoreSettings": "[parameters('restoreSettings')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('infrastructureEncryption', 'Enabled', 'kekIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), createObject('useSystemAssignedIdentity', empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))), 'keyVaultProperties', createObject('keyUri', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]" + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('infrastructureEncryption', 'Enabled', 'kekIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), createObject('useSystemAssignedIdentity', empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))), 'keyVaultProperties', createObject('keyUri', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]" }, "dependsOn": [ "cMKKeyVault::cMKKey" @@ -2297,10 +2297,10 @@ "value": "[parameters('name')]" }, "storageModelType": { - "value": "[parameters('backupStorageConfig').storageModelType]" + "value": "[tryGet(parameters('backupStorageConfig'), 'storageModelType')]" }, "crossRegionRestoreFlag": { - "value": "[parameters('backupStorageConfig').crossRegionRestoreFlag]" + "value": "[tryGet(parameters('backupStorageConfig'), 'crossRegionRestoreFlag')]" } }, "template": { @@ -2953,6 +2953,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-rsv-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/relay/namespace/main.bicep b/avm/res/relay/namespace/main.bicep index b20ac876a2..5de0130cc8 100644 --- a/avm/res/relay/namespace/main.bicep +++ b/avm/res/relay/namespace/main.bicep @@ -218,15 +218,10 @@ resource namespace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(namespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/relay/namespace/main.json b/avm/res/relay/namespace/main.json index 9974d7667d..cbfc6ea08c 100644 --- a/avm/res/relay/namespace/main.json +++ b/avm/res/relay/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10302508139309191094" + "templateHash": "7449491400928242470" }, "name": "Relay Namespaces", "description": "This module deploys a Relay Namespace" @@ -1931,6 +1931,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-namespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index 0861cba24b..ef6fcf6ca5 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -378,15 +378,10 @@ resource serviceBusNamespace_diagnosticSettings 'Microsoft.Insights/diagnosticSe module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-serviceBusNamespace-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(serviceBusNamespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index f8e1241c21..3f9fba0140 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5605645467272297098" + "version": "0.33.13.18514", + "templateHash": "7837803421760030856" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace." @@ -1865,8 +1865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11843582332513126053" + "version": "0.33.13.18514", + "templateHash": "14374854229940696860" }, "name": "Service Bus Namespace Authorization Rules", "description": "This module deploys a Service Bus Namespace Authorization Rule." @@ -1968,8 +1968,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "953280430524155799" + "version": "0.33.13.18514", + "templateHash": "17399088623294854705" }, "name": "Service Bus Namespace Disaster Recovery Configs", "description": "This module deploys a Service Bus Namespace Disaster Recovery Config" @@ -2072,8 +2072,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1185508580247696644" + "version": "0.33.13.18514", + "templateHash": "16312291233623934616" }, "name": "Service Bus Namespace Migration Configuration", "description": "This module deploys a Service Bus Namespace Migration Configuration." @@ -2176,8 +2176,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "286643555978658471" + "version": "0.33.13.18514", + "templateHash": "17182735425325794067" }, "name": "Service Bus Namespace Network Rule Sets", "description": "This module deploys a ServiceBus Namespace Network Rule Set." @@ -2375,8 +2375,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2963369845110660460" + "version": "0.33.13.18514", + "templateHash": "4119251299070252186" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue." @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4106322932640263973" + "version": "0.33.13.18514", + "templateHash": "7526885294323298219" }, "name": "Service Bus Namespace Queue Authorization Rules", "description": "This module deploys a Service Bus Namespace Queue Authorization Rule." @@ -2954,8 +2954,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4751434073420963969" + "version": "0.33.13.18514", + "templateHash": "15157624750923042287" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic." @@ -3636,8 +3636,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9054915416075794466" + "version": "0.33.13.18514", + "templateHash": "8184508565282070207" }, "name": "Service Bus Namespace Topic Authorization Rules", "description": "This module deploys a Service Bus Namespace Topic Authorization Rule." @@ -3789,8 +3789,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3178099276922515439" + "version": "0.33.13.18514", + "templateHash": "13380479271935330273" }, "name": "Service Bus Namespace Topic Subscription", "description": "This module deploys a Service Bus Namespace Topic Subscription." @@ -4183,8 +4183,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2622393872040722258" + "version": "0.33.13.18514", + "templateHash": "4831934980907711659" }, "name": "Service Bus Namespace Topic Subscription Rule", "description": "This module deploys a Service Bus Namespace Topic Subscription Rule." @@ -4375,6 +4375,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-serviceBusNamespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 363dd816ee..0e9d3a62cb 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -343,15 +343,10 @@ module server_elasticPools 'elastic-pool/main.bicep' = [ module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-server-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(server.id, '/'))}-${privateEndpoint.?service ?? 'sqlServer'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index cc2f195da1..070453a235 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "2433863119343202155" + "templateHash": "11930227683565093918" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server." @@ -3685,6 +3685,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-server-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 91ee043a7e..66b9fc6703 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -502,15 +502,10 @@ resource storageAccount_roleAssignments 'Microsoft.Authorization/roleAssignments module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-storageAccount-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index b3193cf49f..0d22a420a4 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "17960260729884623690" + "version": "0.33.13.18514", + "templateHash": "3424685276444889234" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -1451,6 +1451,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2222,8 +2224,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10504956743360699891" + "version": "0.33.13.18514", + "templateHash": "4967204006599351003" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." @@ -2331,8 +2333,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5655292159520921149" + "version": "0.33.13.18514", + "templateHash": "2528560857012083896" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." @@ -2569,8 +2571,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2058460323623594433" + "version": "0.33.13.18514", + "templateHash": "14339432978450199921" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -3036,8 +3038,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.33.13.18514", + "templateHash": "10816586207828434096" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -3325,8 +3327,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.33.13.18514", + "templateHash": "2769922037435749045" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." @@ -3505,8 +3507,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3168394810831105529" + "version": "0.33.13.18514", + "templateHash": "987643333058038389" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -3859,8 +3861,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12044655551245282190" + "version": "0.33.13.18514", + "templateHash": "15193761941438215308" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." @@ -4294,8 +4296,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1736438454543575457" + "version": "0.33.13.18514", + "templateHash": "8158577333548255612" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -4613,8 +4615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6383154227554431205" + "version": "0.33.13.18514", + "templateHash": "9877120144610775153" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." @@ -4883,8 +4885,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12583903411447171294" + "version": "0.33.13.18514", + "templateHash": "541986423744885003" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -5199,8 +5201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1369356397929898951" + "version": "0.33.13.18514", + "templateHash": "11234204519679347949" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." @@ -5453,8 +5455,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2275047425860597278" + "version": "0.33.13.18514", + "templateHash": "14510275109257916717" } }, "definitions": { diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index fb3e2fefa9..2bc090c5f4 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -352,15 +352,10 @@ module workspace_firewallRules 'firewall-rules/main.bicep' = [ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.9.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 6982279774..c58cd50e2f 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17815795427207375469" + "version": "0.33.13.18514", + "templateHash": "4395970160208281653" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace." @@ -1067,8 +1067,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1357925296617686979" + "version": "0.33.13.18514", + "templateHash": "18124614252428623156" }, "name": "Synapse Workspace Integration Runtimes", "description": "This module deploys a Synapse Workspace Integration Runtime." @@ -1166,8 +1166,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5168623611246542439" + "version": "0.33.13.18514", + "templateHash": "4658877837973777703" } }, "parameters": { @@ -1254,8 +1254,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9285184380796627148" + "version": "0.33.13.18514", + "templateHash": "18134135357281825028" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key." @@ -1360,8 +1360,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7870636790140091356" + "version": "0.33.13.18514", + "templateHash": "6961828897310133803" }, "name": "Synapse Workspaces Administrators", "description": "This module deploys Synapse Workspaces Administrators." @@ -1474,8 +1474,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3403338957196025610" + "version": "0.33.13.18514", + "templateHash": "4218971273589711859" }, "name": "Synapse Workspaces Firewall Rules", "description": "This module deploys Synapse Workspaces Firewall Rules." @@ -1554,6 +1554,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 39729295db..8d62479680 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -484,15 +484,10 @@ resource app_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01 module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-app-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 24e3af3ecf..dec2e20b5f 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11078104901059265686" + "version": "0.33.13.18514", + "templateHash": "1510038828157796019" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App." @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6410547302386858558" + "version": "0.33.13.18514", + "templateHash": "3773744005628323179" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting." @@ -1246,8 +1246,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11113399312695963924" + "version": "0.33.13.18514", + "templateHash": "12604769145311962317" }, "name": "Site Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration." @@ -1349,8 +1349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7848470524705559903" + "version": "0.33.13.18514", + "templateHash": "10353279242186955906" }, "name": "Site logs Config", "description": "This module deploys a Site logs Configuration." @@ -1440,8 +1440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17229460238192107968" + "version": "0.33.13.18514", + "templateHash": "15063123874711680503" }, "name": "Site Web Config", "description": "This module deploys web settings configuration available under sites/config name: web." @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "18138875055545072289" + "version": "0.33.13.18514", + "templateHash": "10653723640225381902" }, "name": "Site Deployment Extension ", "description": "This module deploys a Site extension for MSDeploy." @@ -1748,8 +1748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12729018089714646839" + "version": "0.33.13.18514", + "templateHash": "15803822027091526791" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot." @@ -2784,8 +2784,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "10124842813698211071" + "version": "0.33.13.18514", + "templateHash": "6379851449588708934" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting." @@ -2961,8 +2961,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7246612330843636406" + "version": "0.33.13.18514", + "templateHash": "4192061847386712633" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration." @@ -3081,8 +3081,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13728299747043713048" + "version": "0.33.13.18514", + "templateHash": "13483227944246460692" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy." @@ -3205,8 +3205,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1399655044962772411" + "version": "0.33.13.18514", + "templateHash": "17428168852439619558" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay." @@ -3309,8 +3309,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "18138875055545072289" + "version": "0.33.13.18514", + "templateHash": "10653723640225381902" }, "name": "Site Deployment Extension ", "description": "This module deploys a Site extension for MSDeploy." @@ -3379,6 +3379,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-slot-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -4230,8 +4232,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7686713695944836722" + "version": "0.33.13.18514", + "templateHash": "1499299399255876013" }, "name": "Web Site Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Basic Publishing Credentials Policy." @@ -4345,8 +4347,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6378705059063789109" + "version": "0.33.13.18514", + "templateHash": "3864138738153964150" }, "name": "Web/Function Apps Hybrid Connection Relay", "description": "This module deploys a Site Hybrid Connection Namespace Relay." @@ -4426,6 +4428,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-app-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index 56758d1f5c..f41579ba90 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -377,15 +377,10 @@ resource slot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-0 module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-slot-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites-${slot.name}'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index 06e32b96c3..f7a5a18c95 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12729018089714646839" + "version": "0.33.13.18514", + "templateHash": "15803822027091526791" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot." @@ -1041,8 +1041,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "10124842813698211071" + "version": "0.33.13.18514", + "templateHash": "6379851449588708934" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting." @@ -1218,8 +1218,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7246612330843636406" + "version": "0.33.13.18514", + "templateHash": "4192061847386712633" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration." @@ -1338,8 +1338,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13728299747043713048" + "version": "0.33.13.18514", + "templateHash": "13483227944246460692" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy." @@ -1462,8 +1462,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1399655044962772411" + "version": "0.33.13.18514", + "templateHash": "17428168852439619558" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay." @@ -1566,8 +1566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "18138875055545072289" + "version": "0.33.13.18514", + "templateHash": "10653723640225381902" }, "name": "Site Deployment Extension ", "description": "This module deploys a Site extension for MSDeploy." @@ -1636,6 +1636,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-slot-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep index 2eab05ab58..f3387107ab 100644 --- a/avm/res/web/static-site/main.bicep +++ b/avm/res/web/static-site/main.bicep @@ -247,15 +247,10 @@ resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@202 module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-staticSite-PrivateEndpoint-${index}' - scope: !empty(privateEndpoint.?resourceGroupResourceId) - ? resourceGroup( - split((privateEndpoint.?resourceGroupResourceId ?? '//'), '/')[2], - split((privateEndpoint.?resourceGroupResourceId ?? '////'), '/')[4] - ) - : resourceGroup( - split((privateEndpoint.?subnetResourceId ?? '//'), '/')[2], - split((privateEndpoint.?subnetResourceId ?? '////'), '/')[4] - ) + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json index 33de8d5d87..4ce780a6b8 100644 --- a/avm/res/web/static-site/main.json +++ b/avm/res/web/static-site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7497371071073742378" + "version": "0.33.13.18514", + "templateHash": "5409964564427816888" }, "name": "Static Web Apps", "description": "This module deploys a Static Web App." @@ -701,8 +701,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8275628450081231495" + "version": "0.33.13.18514", + "templateHash": "6117646567314030149" }, "name": "Static Web App Site Linked Backends", "description": "This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property." @@ -802,8 +802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12709536246529127181" + "version": "0.33.13.18514", + "templateHash": "7998567469554918663" }, "name": "Static Web App Site Config", "description": "This module deploys a Static Web App Site Config." @@ -896,8 +896,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12709536246529127181" + "version": "0.33.13.18514", + "templateHash": "7998567469554918663" }, "name": "Static Web App Site Config", "description": "This module deploys a Static Web App Site Config." @@ -991,8 +991,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2208155866464160058" + "version": "0.33.13.18514", + "templateHash": "3686358111815300788" }, "name": "Static Web App Site Custom Domains", "description": "This module deploys a Static Web App Site Custom Domain." @@ -1065,6 +1065,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-staticSite-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" From e719dff36db3f0f0eb79ed5d24362a589f1a5995 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:56:06 +0100 Subject: [PATCH 15/53] feat: Update `avm/res/insights/metric-alert` to latest UDT (#4425) ## Description Update to latest UDT ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.insights.metric-alert](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml/badge.svg?branch=users%2Fkrbar%2FmetricAlrtUDT)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/insights/metric-alert/README.md | 9 ++ avm/res/insights/metric-alert/main.bicep | 30 +---- avm/res/insights/metric-alert/main.json | 158 ++++++++++++----------- 3 files changed, 94 insertions(+), 103 deletions(-) diff --git a/avm/res/insights/metric-alert/README.md b/avm/res/insights/metric-alert/README.md index 6f390fc6c6..7c91321ffc 100644 --- a/avm/res/insights/metric-alert/README.md +++ b/avm/res/insights/metric-alert/README.md @@ -8,6 +8,7 @@ This module deploys a Metric Alert. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) - [Data Collection](#Data-Collection) ## Resource Types @@ -792,6 +793,14 @@ the period of time (in ISO 8601 duration format) that is used to monitor alert a | `resourceGroupName` | string | The resource group the metric alert was deployed into. | | `resourceId` | string | The resource ID of the metric alert. | +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/insights/metric-alert/main.bicep b/avm/res/insights/metric-alert/main.bicep index 5fd362a279..700a26304d 100644 --- a/avm/res/insights/metric-alert/main.bicep +++ b/avm/res/insights/metric-alert/main.bicep @@ -66,8 +66,9 @@ param actions array = [] @description('Required. Maps to the \'odata.type\' field. Specifies the type of the alert criteria.') param criteria alertType +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? @description('Optional. Tags of the resource.') param tags object? @@ -180,36 +181,11 @@ output resourceId string = metricAlert.id @description('The location the resource was deployed into.') output location string = metricAlert.location + // =============== // // Definitions // // =============== // -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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? -}[]? - @discriminator('odata.type') type alertType = alertWebtestType | alertResourceType | alertMultiResourceType type alertResourceType = { diff --git a/avm/res/insights/metric-alert/main.json b/avm/res/insights/metric-alert/main.json index 0a13398ff1..22a05a4477 100644 --- a/avm/res/insights/metric-alert/main.json +++ b/avm/res/insights/metric-alert/main.json @@ -5,86 +5,13 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8398195355091608578" + "version": "0.33.93.31351", + "templateHash": "5542299104437497073" }, "name": "Metric Alerts", "description": "This module deploys a Metric Alert." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } - } - } - }, - "nullable": true - }, "alertType": { "type": "object", "discriminator": { @@ -149,6 +76,81 @@ "type": "string" } } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -268,7 +270,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } From 6eea2f702260c51467c885ef4b5a1d79c55fec0e Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:56:35 +0100 Subject: [PATCH 16/53] feat: Update `avm/res/insights/webtest` to latest UDT (#4424) ## Description - update to latest UDT ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.insights.webtest](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml/badge.svg?branch=users%2Fkrbar%2FwebtestUDT)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/insights/webtest/README.md | 9 ++ avm/res/insights/webtest/main.bicep | 44 +------- avm/res/insights/webtest/main.json | 156 +++++++++++++++------------- 3 files changed, 97 insertions(+), 112 deletions(-) diff --git a/avm/res/insights/webtest/README.md b/avm/res/insights/webtest/README.md index f9f5197cd4..13f0ff3ee8 100644 --- a/avm/res/insights/webtest/README.md +++ b/avm/res/insights/webtest/README.md @@ -8,6 +8,7 @@ This module deploys a Web Test. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) - [Data Collection](#Data-Collection) ## Resource Types @@ -775,6 +776,14 @@ The collection of validation rule properties. | `resourceGroupName` | string | The resource group the resource was deployed into. | | `resourceId` | string | The resource ID of the webtest. | +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/insights/webtest/main.bicep b/avm/res/insights/webtest/main.bicep index b8c0ad8b2a..e6563da00f 100644 --- a/avm/res/insights/webtest/main.bicep +++ b/avm/res/insights/webtest/main.bicep @@ -70,11 +70,13 @@ param validationRules object = {} @sys.description('Optional. An XML configuration specification for a WebTest.') param configuration object? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @sys.description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @sys.description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? @sys.description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true @@ -185,41 +187,3 @@ output resourceGroupName string = resourceGroup().name @sys.description('The location the resource was deployed into.') output location string = webtest.location - -// =============== // -// Definitions // -// =============== // - -type lockType = { - @sys.description('Optional. Specify the name of lock.') - name: string? - - @sys.description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - roleDefinitionIdOrName: string - - @sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') - principalId: string - - @sys.description('Optional. The principal type of the assigned principal ID.') - principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? - - @sys.description('Optional. The description of the role assignment.') - description: string? - - @sys.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? - - @sys.description('Optional. Version of the condition.') - conditionVersion: '2.0'? - - @sys.description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? diff --git a/avm/res/insights/webtest/main.json b/avm/res/insights/webtest/main.json index f1d2d696dd..b144dea6d3 100644 --- a/avm/res/insights/webtest/main.json +++ b/avm/res/insights/webtest/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13500385176780435448" + "version": "0.33.93.31351", + "templateHash": "13066561667388649172" }, "name": "Web Tests", "description": "This module deploys a Web Test." @@ -35,80 +35,87 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -243,12 +250,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } From 3db5313ae0361060a66c1eca4837d58d2177233e Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:56:56 +0100 Subject: [PATCH 17/53] feat: Update `avm/res/event-grid/namespace` to latest UDT + output fix (#4423) ## Description - Update to latest UDTs - Fix of the private endpoint output / Update to the `privateEndpointOutputType` UDT ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.event-grid.namespace](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml/badge.svg?branch=user%2Fkrbar%2FeventGridUDT)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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`. - [x] 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 --- avm/res/event-grid/namespace/README.md | 59 +- .../namespace/ca-certificate/main.json | 4 +- .../namespace/client-group/main.json | 4 +- avm/res/event-grid/namespace/client/main.json | 4 +- avm/res/event-grid/namespace/main.bicep | 217 +- avm/res/event-grid/namespace/main.json | 1877 +++++++++-------- .../namespace/permission-binding/main.json | 4 +- .../namespace/topic-space/README.md | 9 + .../namespace/topic-space/main.bicep | 33 +- .../namespace/topic-space/main.json | 148 +- avm/res/event-grid/namespace/topic/README.md | 9 + .../topic/event-subscription/README.md | 9 + .../topic/event-subscription/main.bicep | 33 +- .../topic/event-subscription/main.json | 148 +- avm/res/event-grid/namespace/topic/main.bicep | 44 +- avm/res/event-grid/namespace/topic/main.json | 304 +-- avm/res/event-grid/namespace/version.json | 2 +- 17 files changed, 1436 insertions(+), 1472 deletions(-) diff --git a/avm/res/event-grid/namespace/README.md b/avm/res/event-grid/namespace/README.md index 0917cedaa2..26566a7f4d 100644 --- a/avm/res/event-grid/namespace/README.md +++ b/avm/res/event-grid/namespace/README.md @@ -2051,7 +2051,7 @@ The diagnostic settings of the service. | [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | | [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | | [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | -| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of the diagnostic setting. | | [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | | [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | @@ -2161,7 +2161,7 @@ Enable or disable the category explicitly. Default is `true`. ### Parameter: `diagnosticSettings.name` -The name of diagnostic setting. +The name of the diagnostic setting. - Required: No - Type: string @@ -2258,7 +2258,7 @@ The managed identity definition for this resource. | Parameter | Type | Description | | :-- | :-- | :-- | | [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | -| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | ### Parameter: `managedIdentities.systemAssigned` @@ -2269,7 +2269,7 @@ Enables system assigned managed identity on the resource. ### Parameter: `managedIdentities.userAssignedResourceIds` -The resource ID(s) to assign to the resource. +The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. - Required: No - Type: array @@ -2322,22 +2322,22 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the Private Endpoint IP configuration is included. | | [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | -| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | +| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the Private Endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. | | [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | -| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | +| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the Private Endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | -| [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`name`](#parameter-privateendpointsname) | string | The name of the Private Endpoint. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS Zone Group to configure for the Private Endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | -| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | +| [`resourceGroupResourceId`](#parameter-privateendpointsresourcegroupresourceid) | string | The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | -| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | -| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. | +| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/Resource Groups in this deployment. | ### Parameter: `privateEndpoints.subnetResourceId` @@ -2350,7 +2350,7 @@ Resource ID of the subnet where the endpoint needs to be created. ### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` -Application security groups in which the private endpoint IP configuration is included. +Application security groups in which the Private Endpoint IP configuration is included. - Required: No - Type: array @@ -2398,7 +2398,7 @@ FQDN that resolves to private endpoint IP address. ### Parameter: `privateEndpoints.customNetworkInterfaceName` -The custom name of the network interface attached to the private endpoint. +The custom name of the network interface attached to the Private Endpoint. - Required: No - Type: string @@ -2416,7 +2416,7 @@ Enable/Disable usage telemetry for module. ### Parameter: `privateEndpoints.ipConfigurations` -A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. +A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. - Required: No - Type: array @@ -2494,7 +2494,7 @@ If Manual Private Link Connection is required. ### Parameter: `privateEndpoints.location` -The location to deploy the private endpoint to. +The location to deploy the Private Endpoint to. - Required: No - Type: string @@ -2554,7 +2554,7 @@ A message passed to the owner of the remote resource with the manual connection ### Parameter: `privateEndpoints.name` -The name of the private endpoint. +The name of the Private Endpoint. - Required: No - Type: string @@ -2563,7 +2563,7 @@ The name of the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroup` -The private DNS zone group to configure for the private endpoint. +The private DNS Zone Group to configure for the Private Endpoint. - Required: No - Type: object @@ -2574,7 +2574,7 @@ The private DNS zone group to configure for the private endpoint. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. | **Optional parameters** @@ -2584,7 +2584,7 @@ The private DNS zone group to configure for the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` -The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. +The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. - Required: Yes - Type: array @@ -2601,7 +2601,7 @@ The private DNS zone groups to associate the private endpoint. A DNS zone group | Parameter | Type | Description | | :-- | :-- | :-- | -| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS Zone Group config. | ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` @@ -2614,7 +2614,7 @@ The resource id of the private DNS zone. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group config. +The name of the private DNS Zone Group config. - Required: No - Type: string @@ -2639,9 +2639,9 @@ The name of the private link connection to create. - MinValue: 1 - MaxValue: 8 -### Parameter: `privateEndpoints.resourceGroupName` +### Parameter: `privateEndpoints.resourceGroupResourceId` -Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. +The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. - Required: No - Type: string @@ -2666,7 +2666,7 @@ Array of role assignments to create. - `'Owner'` - `'Private DNS Zone Contributor'` - `'Reader'` - - `'Role Based Access Control Administrator (Preview)'` + - `'Role Based Access Control Administrator'` **Required parameters** @@ -2776,7 +2776,7 @@ The principal type of the assigned principal ID. ### Parameter: `privateEndpoints.service` -The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". +The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. - Required: No - Type: string @@ -2785,7 +2785,7 @@ The subresource to deploy the private endpoint for. For example "vault", "mysqlS ### Parameter: `privateEndpoints.tags` -Tags to be applied on all resources/resource groups in this deployment. +Tags to be applied on all resources/Resource Groups in this deployment. - Required: No - Type: object @@ -3018,7 +3018,8 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-grid/namespace/ca-certificate/main.json b/avm/res/event-grid/namespace/ca-certificate/main.json index 30528042a1..2e6b66447f 100644 --- a/avm/res/event-grid/namespace/ca-certificate/main.json +++ b/avm/res/event-grid/namespace/ca-certificate/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13580243066591608578" + "version": "0.33.93.31351", + "templateHash": "5500161725034179010" }, "name": "Eventgrid Namespace CA Certificates", "description": "This module deploys an Eventgrid Namespace CA Certificate." diff --git a/avm/res/event-grid/namespace/client-group/main.json b/avm/res/event-grid/namespace/client-group/main.json index e42be83951..e15bfc0d69 100644 --- a/avm/res/event-grid/namespace/client-group/main.json +++ b/avm/res/event-grid/namespace/client-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12442980068595434063" + "version": "0.33.93.31351", + "templateHash": "15034210943727152335" }, "name": "Eventgrid Namespace Client Groups", "description": "This module deploys an Eventgrid Namespace Client Group." diff --git a/avm/res/event-grid/namespace/client/main.json b/avm/res/event-grid/namespace/client/main.json index 09c397004f..7bc353b1c0 100644 --- a/avm/res/event-grid/namespace/client/main.json +++ b/avm/res/event-grid/namespace/client/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13739700279576908785" + "version": "0.33.93.31351", + "templateHash": "310861179374860968" }, "name": "Eventgrid Namespace Clients", "description": "This module deploys an Eventgrid Namespace Client." diff --git a/avm/res/event-grid/namespace/main.bicep b/avm/res/event-grid/namespace/main.bicep index c4c74cbb8f..65518fdc16 100644 --- a/avm/res/event-grid/namespace/main.bicep +++ b/avm/res/event-grid/namespace/main.bicep @@ -15,8 +15,9 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true +import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The managed identity definition for this resource.') -param managedIdentities managedIdentitiesType +param managedIdentities managedIdentityAllType? @description('Optional. Allows the user to specify if the namespace resource supports zone-redundancy capability or not. If this property is not specified explicitly by the user, its default value depends on the following conditions: a. For Availability Zones enabled regions - The default property value would be true. b. For non-Availability Zones enabled regions - The default property value would be false. Once specified, this property cannot be updated.') param isZoneRedundant bool? @@ -32,17 +33,21 @@ param publicNetworkAccess string? @description('Optional. This can be used to restrict traffic from specific IPs instead of all IPs. Note: These are considered only if PublicNetworkAccess is enabled.') param inboundIpRules array? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The diagnostic settings of the service.') -param diagnosticSettings diagnosticSettingType +param diagnosticSettings diagnosticSettingFullType[]? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? +import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') -param privateEndpoints privateEndpointType +param privateEndpoints privateEndpointSingleServiceType[]? @allowed([ 'Enabled' @@ -267,10 +272,13 @@ resource namespace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 } ] -module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ +module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' - scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(namespace.id, '/'))}-${privateEndpoint.?service ?? 'topic'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -444,191 +452,42 @@ output systemAssignedMIPrincipalId string? = namespace.?identity.?principalId output topicResourceIds array = [ for index in range(0, length(topics ?? [])): namespace_topics[index].outputs.resourceId ] - @description('The private endpoints of the EventGrid Namespace.') -output privateEndpoints array = [ +output privateEndpoints privateEndpointOutputType[] = [ for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { name: namespace_privateEndpoints[i].outputs.name resourceId: namespace_privateEndpoints[i].outputs.resourceId - groupId: namespace_privateEndpoints[i].outputs.groupId - customDnsConfig: namespace_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: namespace_privateEndpoints[i].outputs.networkInterfaceIds + groupId: namespace_privateEndpoints[i].outputs.?groupId! + customDnsConfigs: namespace_privateEndpoints[i].outputs.customDnsConfigs + networkInterfaceResourceIds: namespace_privateEndpoints[i].outputs.networkInterfaceResourceIds } ] -// ================ // -// Definitions // -// ================ // -// -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.') - userAssignedResourceIds: string[]? -}? - -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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'? +// =============== // +// Definitions // +// =============== // - @description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? +@export() +@description('The type for a private endpoint output.') +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string -type privateEndpointType = { - @description('Optional. The name of the private endpoint.') - name: string? + @description('The resource ID of the private endpoint.') + resourceId: string - @description('Optional. The location to deploy the private endpoint to.') - location: string? + @description('The group Id for the private endpoint Group.') + groupId: string? - @description('Optional. The name of the private link connection to create.') - privateLinkServiceConnectionName: string? - - @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') - service: string? - - @description('Required. Resource ID of the subnet where the endpoint needs to be created.') - subnetResourceId: string - - @description('Optional. The private DNS zone group to configure for the private endpoint.') - privateDnsZoneGroup: { - @description('Optional. The name of the Private DNS Zone Group.') - name: string? - - @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneGroupConfigs: { - @description('Optional. The name of the private DNS zone group config.') - name: string? - - @description('Required. The resource id of the private DNS zone.') - privateDnsZoneResourceId: string - }[] - }? - - @description('Optional. If Manual Private Link Connection is required.') - isManualConnection: bool? - - @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') - @maxLength(140) - manualConnectionRequestMessage: string? - - @description('Optional. Custom DNS configurations.') + @description('The custom DNS configurations of the private endpoint.') customDnsConfigs: { - @description('Optional. FQDN that resolves to private endpoint IP address.') + @description('FQDN that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private IP addresses of the private endpoint.') + @description('A list of private IP addresses of the private endpoint.') 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: { - @description('Required. The name of the resource that is unique within a resource group.') - name: string - - @description('Required. Properties of private endpoint IP configurations.') - properties: { - @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') - groupId: string + }[] - @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') - memberName: string - - @description('Required. A private IP address obtained from the private endpoint\'s subnet.') - 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: lockType - - @description('Optional. Array of role assignments to create.') - roleAssignments: roleAssignmentType - - @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') - tags: object? - - @description('Optional. Enable/Disable usage telemetry for module.') - enableTelemetry: bool? - - @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') - resourceGroupName: string? -}[]? - -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. Enable or disable the category explicitly. Default is `true`.') - enabled: bool? - }[]? - - @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric 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. Enable or disable the category explicitly. Default is `true`.') - enabled: bool? - }[]? - - @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')? - - @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? -}[]? + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} diff --git a/avm/res/event-grid/namespace/main.json b/avm/res/event-grid/namespace/main.json index 26478a4e59..710d97699d 100644 --- a/avm/res/event-grid/namespace/main.json +++ b/avm/res/event-grid/namespace/main.json @@ -5,473 +5,582 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "13579387788541405387" + "version": "0.33.93.31351", + "templateHash": "4896325805194987111" }, "name": "Event Grid Namespaces", "description": "This module deploys an Event Grid Namespace." }, "definitions": { - "managedIdentitiesType": { + "privateEndpointOutputType": { "type": "object", "properties": { - "systemAssigned": { - "type": "bool", + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." + "description": "The group Id for the private endpoint Group." } }, - "userAssignedResourceIds": { + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { "type": "array", "items": { "type": "string" }, - "nullable": true, "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource." + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for a private endpoint output." + } }, - "lockType": { + "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { - "name": { + "fqdn": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Optional. FQDN that resolves to private endpoint IP address." } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. Specify the type of lock." + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateEndpointType": { - "type": "array", - "items": { - "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." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "Optional. The name of the private DNS Zone Group config." } }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group config." - } - }, - "privateDnsZoneResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of the private DNS zone." - } - } - } - }, + "privateDnsZoneResourceId": { + "type": "string", "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } + "description": "Required. The resource id of the private DNS zone." } } - }, - "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": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." } }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." - } + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "diagnosticSettingType": { - "type": "array", - "items": { - "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." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the 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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } - }, - "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." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric 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." - } + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric 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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "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." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "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." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" }, - "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." - } + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" }, - "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." - } + "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" }, - "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." - } + "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": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "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." - } + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -505,7 +614,8 @@ } }, "managedIdentities": { - "$ref": "#/definitions/managedIdentitiesType", + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, "metadata": { "description": "Optional. The managed identity definition for this resource." } @@ -538,24 +648,37 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { "description": "Optional. The diagnostic settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } }, "privateEndpoints": { - "$ref": "#/definitions/privateEndpointType", + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, "metadata": { "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } @@ -813,7 +936,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-namespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -866,12 +990,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -893,80 +1016,118 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "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\"." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "lockType": { "type": "object", @@ -991,182 +1152,108 @@ } } }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } - }, - "nullable": true + } }, - "privateDnsZoneGroupConfigType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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\"." } }, - "privateDnsZoneResourceId": { + "conditionVersion": { "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." } } }, "metadata": { + "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -1186,6 +1273,9 @@ }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -1199,7 +1289,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -1220,12 +1314,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -1238,21 +1337,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -1281,7 +1392,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -1289,7 +1400,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1395,12 +1506,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -1478,10 +1588,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -1543,26 +1650,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -1614,8 +1728,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15793214931835923034" + "version": "0.33.93.31351", + "templateHash": "3750609028879158720" }, "name": "Eventgrid Namespace Topics", "description": "This module deploys an Eventgrid Namespace Topic." @@ -1644,80 +1758,87 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -1734,13 +1855,18 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -1896,85 +2022,87 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "17184382679184813652" + "version": "0.33.93.31351", + "templateHash": "2786410706674700781" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription." }, "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -2018,7 +2146,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -2188,8 +2320,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "16684000080098402376" + "version": "0.33.93.31351", + "templateHash": "5500161725034179010" }, "name": "Eventgrid Namespace CA Certificates", "description": "This module deploys an Eventgrid Namespace CA Certificate." @@ -2314,8 +2446,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "11246395673519136496" + "version": "0.33.93.31351", + "templateHash": "310861179374860968" }, "name": "Eventgrid Namespace Clients", "description": "This module deploys an Eventgrid Namespace Client." @@ -2476,8 +2608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15188233906898213813" + "version": "0.33.93.31351", + "templateHash": "15034210943727152335" }, "name": "Eventgrid Namespace Client Groups", "description": "This module deploys an Eventgrid Namespace Client Group." @@ -2593,85 +2725,87 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "16328606795351825014" + "version": "0.33.93.31351", + "templateHash": "8680886384150003323" }, "name": "Eventgrid Namespace Topic Spaces", "description": "This module deploys an Eventgrid Namespace Topic Space." }, "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -2702,7 +2836,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -2841,8 +2979,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2140320336515533966" + "version": "0.33.93.31351", + "templateHash": "17347618187908890991" }, "name": "Eventgrid Namespace Permissions Bindings", "description": "This module deploys an Eventgrid Namespace Permission Binding." @@ -2990,6 +3128,9 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the EventGrid Namespace." }, @@ -2998,9 +3139,9 @@ "input": { "name": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } } diff --git a/avm/res/event-grid/namespace/permission-binding/main.json b/avm/res/event-grid/namespace/permission-binding/main.json index c47335a6b8..5e0d007989 100644 --- a/avm/res/event-grid/namespace/permission-binding/main.json +++ b/avm/res/event-grid/namespace/permission-binding/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14853334081305348305" + "version": "0.33.93.31351", + "templateHash": "17347618187908890991" }, "name": "Eventgrid Namespace Permissions Bindings", "description": "This module deploys an Eventgrid Namespace Permission Binding." diff --git a/avm/res/event-grid/namespace/topic-space/README.md b/avm/res/event-grid/namespace/topic-space/README.md index 442efe3335..900dae5143 100644 --- a/avm/res/event-grid/namespace/topic-space/README.md +++ b/avm/res/event-grid/namespace/topic-space/README.md @@ -7,6 +7,7 @@ This module deploys an Eventgrid Namespace Topic Space. - [Resource Types](#Resource-Types) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) ## Resource Types @@ -183,3 +184,11 @@ The principal type of the assigned principal ID. | `name` | string | The name of the Topic Space. | | `resourceGroupName` | string | The name of the resource group the Topic Space was created in. | | `resourceId` | string | The resource ID of the Topic Space. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | diff --git a/avm/res/event-grid/namespace/topic-space/main.bicep b/avm/res/event-grid/namespace/topic-space/main.bicep index 2020d61d90..6a58bf6bd3 100644 --- a/avm/res/event-grid/namespace/topic-space/main.bicep +++ b/avm/res/event-grid/namespace/topic-space/main.bicep @@ -14,8 +14,9 @@ param description string? @sys.description('Required. The topic filters in the Topic Space.') param topicTemplates array +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @sys.description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? var builtInRoleNames = { 'Azure Resource Notifications System Topics Subscriber': subscriptionResourceId( @@ -119,33 +120,3 @@ output name string = topicSpace.name @sys.description('The name of the resource group the Topic Space was created in.') output resourceGroupName string = resourceGroup().name - -// ================ // -// Definitions // -// ================ // - -type roleAssignmentType = { - @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - roleDefinitionIdOrName: string - - @sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') - principalId: string - - @sys.description('Optional. The principal type of the assigned principal ID.') - principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? - - @sys.description('Optional. The description of the role assignment.') - description: string? - - @sys.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? - - @sys.description('Optional. Version of the condition.') - conditionVersion: '2.0'? - - @sys.description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? diff --git a/avm/res/event-grid/namespace/topic-space/main.json b/avm/res/event-grid/namespace/topic-space/main.json index 3bfb89e09c..6ae1ca7d77 100644 --- a/avm/res/event-grid/namespace/topic-space/main.json +++ b/avm/res/event-grid/namespace/topic-space/main.json @@ -5,85 +5,87 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "936984304688301643" + "version": "0.33.93.31351", + "templateHash": "8680886384150003323" }, "name": "Eventgrid Namespace Topic Spaces", "description": "This module deploys an Eventgrid Namespace Topic Space." }, "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -114,7 +116,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } diff --git a/avm/res/event-grid/namespace/topic/README.md b/avm/res/event-grid/namespace/topic/README.md index 8c64d5e05f..a8d8f52ac5 100644 --- a/avm/res/event-grid/namespace/topic/README.md +++ b/avm/res/event-grid/namespace/topic/README.md @@ -7,6 +7,7 @@ This module deploys an Eventgrid Namespace Topic. - [Resource Types](#Resource-Types) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) ## Resource Types @@ -273,3 +274,11 @@ The principal type of the assigned principal ID. | `name` | string | The name of the Namespace Topic. | | `resourceGroupName` | string | The name of the resource group the Namespace Topic was created in. | | `resourceId` | string | The resource ID of the Namespace Topic. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | diff --git a/avm/res/event-grid/namespace/topic/event-subscription/README.md b/avm/res/event-grid/namespace/topic/event-subscription/README.md index 3efe652422..7270a86c44 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/README.md +++ b/avm/res/event-grid/namespace/topic/event-subscription/README.md @@ -7,6 +7,7 @@ This module deploys an Event Subscription. - [Resource Types](#Resource-Types) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) ## Resource Types @@ -200,3 +201,11 @@ The principal type of the assigned principal ID. | `name` | string | The name of the Event Subscription. | | `resourceGroupName` | string | The name of the resource group the Event Subscription was created in. | | `resourceId` | string | The resource ID of the Event Subscription. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | diff --git a/avm/res/event-grid/namespace/topic/event-subscription/main.bicep b/avm/res/event-grid/namespace/topic/event-subscription/main.bicep index e75dc08187..dabe48cf5d 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/main.bicep +++ b/avm/res/event-grid/namespace/topic/event-subscription/main.bicep @@ -19,8 +19,9 @@ param eventDeliverySchema string = 'CloudEventSchemaV1_0' @description('Optional. Information about the filter for the Event Subscription.') param filtersConfiguration object? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? var builtInRoleNames = { 'Azure Resource Notifications System Topics Subscriber': subscriptionResourceId( @@ -135,33 +136,3 @@ output name string = eventSubscription.name @description('The name of the resource group the Event Subscription was created in.') output resourceGroupName string = resourceGroup().name - -// ================ // -// Definitions // -// ================ // - -type roleAssignmentType = { - @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - roleDefinitionIdOrName: string - - @sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') - principalId: string - - @sys.description('Optional. The principal type of the assigned principal ID.') - principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? - - @sys.description('Optional. The description of the role assignment.') - description: string? - - @sys.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? - - @sys.description('Optional. Version of the condition.') - conditionVersion: '2.0'? - - @sys.description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? diff --git a/avm/res/event-grid/namespace/topic/event-subscription/main.json b/avm/res/event-grid/namespace/topic/event-subscription/main.json index e202dcea00..28b879c0d4 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/main.json +++ b/avm/res/event-grid/namespace/topic/event-subscription/main.json @@ -5,85 +5,87 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3328924279528017223" + "version": "0.33.93.31351", + "templateHash": "2786410706674700781" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription." }, "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -127,7 +129,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } diff --git a/avm/res/event-grid/namespace/topic/main.bicep b/avm/res/event-grid/namespace/topic/main.bicep index 0962dfd472..9ecb200249 100644 --- a/avm/res/event-grid/namespace/topic/main.bicep +++ b/avm/res/event-grid/namespace/topic/main.bicep @@ -7,11 +7,13 @@ param namespaceName string @description('Required. Name of the topic.') param name string +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? @minValue(1) @maxValue(7) @@ -156,41 +158,3 @@ output name string = topic.name @description('The name of the resource group the Namespace Topic was created in.') output resourceGroupName string = resourceGroup().name - -// ================ // -// Definitions // -// ================ // - -type lockType = { - @sys.description('Optional. Specify the name of lock.') - name: string? - - @sys.description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - roleDefinitionIdOrName: string - - @sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') - principalId: string - - @sys.description('Optional. The principal type of the assigned principal ID.') - principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? - - @sys.description('Optional. The description of the role assignment.') - description: string? - - @sys.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? - - @sys.description('Optional. Version of the condition.') - conditionVersion: '2.0'? - - @sys.description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? diff --git a/avm/res/event-grid/namespace/topic/main.json b/avm/res/event-grid/namespace/topic/main.json index be9052e144..ffffae5995 100644 --- a/avm/res/event-grid/namespace/topic/main.json +++ b/avm/res/event-grid/namespace/topic/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15715688844696636869" + "version": "0.33.93.31351", + "templateHash": "3750609028879158720" }, "name": "Eventgrid Namespace Topics", "description": "This module deploys an Eventgrid Namespace Topic." @@ -35,80 +35,87 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -125,13 +132,18 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -287,85 +299,87 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3328924279528017223" + "version": "0.33.93.31351", + "templateHash": "2786410706674700781" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription." }, "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -409,7 +423,11 @@ } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } diff --git a/avm/res/event-grid/namespace/version.json b/avm/res/event-grid/namespace/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/event-grid/namespace/version.json +++ b/avm/res/event-grid/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] From 49c1a1da3567d36270e51a912427fa49e39d676f Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:57:22 +0100 Subject: [PATCH 18/53] feat: Log Analytics Workspace updates - API versions and new properties (#4418) ## Description - update of the API versions - new parameter `features` of the type `workspaceFeaturesType` added - user defined type: `workspaceFeaturesType` - converted the former parameter `useResourcePermissions` into a property of the `features` parameter: `enableLogAccessUsingOnlyResourcePermissions` to align with the template reference - new properties of the `features` object implemented: - `disableLocalAuth` - `enableDataExport` - `immediatePurgeDataOn30Days` - update of the test cases to reflect new parameters ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.operational-insights.workspace](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml/badge.svg?branch=users%2Fkrbar%2FlawUpdate)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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`. - [x] 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 --- .../operational-insights/workspace/README.md | 139 +++++++++++++----- .../workspace/data-export/README.md | 2 +- .../workspace/data-export/main.bicep | 4 +- .../workspace/data-export/main.json | 8 +- .../workspace/data-source/README.md | 2 +- .../workspace/data-source/main.bicep | 4 +- .../workspace/data-source/main.json | 8 +- .../workspace/linked-service/README.md | 2 +- .../workspace/linked-service/main.bicep | 4 +- .../workspace/linked-service/main.json | 8 +- .../linked-storage-account/README.md | 2 +- .../linked-storage-account/main.bicep | 4 +- .../linked-storage-account/main.json | 8 +- .../operational-insights/workspace/main.bicep | 27 +++- .../operational-insights/workspace/main.json | 116 ++++++++++----- .../workspace/saved-search/README.md | 2 +- .../workspace/saved-search/main.bicep | 4 +- .../workspace/saved-search/main.json | 8 +- .../storage-insight-config/README.md | 2 +- .../storage-insight-config/main.bicep | 4 +- .../storage-insight-config/main.json | 8 +- .../workspace/table/README.md | 2 +- .../workspace/table/main.bicep | 4 +- .../workspace/table/main.json | 8 +- .../workspace/tests/e2e/adv/main.test.bicep | 4 +- .../workspace/tests/e2e/max/main.test.bicep | 11 +- .../tests/e2e/waf-aligned/main.test.bicep | 8 +- .../workspace/version.json | 2 +- 28 files changed, 268 insertions(+), 137 deletions(-) diff --git a/avm/res/operational-insights/workspace/README.md b/avm/res/operational-insights/workspace/README.md index 8ea1cf3c5c..1d86fc74b0 100644 --- a/avm/res/operational-insights/workspace/README.md +++ b/avm/res/operational-insights/workspace/README.md @@ -18,14 +18,14 @@ This module deploys a Log Analytics Workspace. | `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.OperationalInsights/workspaces` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces) | -| `Microsoft.OperationalInsights/workspaces/dataExports` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataExports) | -| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | -| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | -| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | -| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | -| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | -| `Microsoft.OperationalInsights/workspaces/tables` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces/tables) | +| `Microsoft.OperationalInsights/workspaces` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces) | +| `Microsoft.OperationalInsights/workspaces/dataExports` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/dataExports) | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/dataSources) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/linkedServices) | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/linkedStorageAccounts) | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/savedSearches) | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/storageInsightConfigs) | +| `Microsoft.OperationalInsights/workspaces/tables` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/tables) | | `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | | `Microsoft.SecurityInsights/onboardingStates` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.SecurityInsights/2024-03-01/onboardingStates) | @@ -198,6 +198,9 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = useThisWorkspace: true } ] + features: { + enableLogAccessUsingOnlyResourcePermissions: true + } gallerySolutions: [ { name: 'AzureAutomation(oiwadv001)' @@ -338,7 +341,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - useResourcePermissions: true } } ``` @@ -508,6 +510,11 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = } ] }, + "features": { + "value": { + "enableLogAccessUsingOnlyResourcePermissions": true + } + }, "gallerySolutions": { "value": [ { @@ -669,9 +676,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } - }, - "useResourcePermissions": { - "value": true } } } @@ -830,6 +834,9 @@ param diagnosticSettings = [ useThisWorkspace: true } ] +param features = { + enableLogAccessUsingOnlyResourcePermissions: true +} param gallerySolutions = [ { name: 'AzureAutomation(oiwadv001)' @@ -970,7 +977,6 @@ param tags = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } -param useResourcePermissions = true ``` @@ -1161,6 +1167,12 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = workspaceResourceId: '' } ] + features: { + disableLocalAuth: true + enableDataExport: true + enableLogAccessUsingOnlyResourcePermissions: true + immediatePurgeDataOn30Days: true + } gallerySolutions: [ { name: 'AzureAutomation(oiwmax001)' @@ -1348,7 +1360,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - useResourcePermissions: true } } ``` @@ -1481,6 +1492,14 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = } ] }, + "features": { + "value": { + "disableLocalAuth": true, + "enableDataExport": true, + "enableLogAccessUsingOnlyResourcePermissions": true, + "immediatePurgeDataOn30Days": true + } + }, "gallerySolutions": { "value": [ { @@ -1695,9 +1714,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } - }, - "useResourcePermissions": { - "value": true } } } @@ -1821,6 +1837,12 @@ param diagnosticSettings = [ workspaceResourceId: '' } ] +param features = { + disableLocalAuth: true + enableDataExport: true + enableLogAccessUsingOnlyResourcePermissions: true + immediatePurgeDataOn30Days: true +} param gallerySolutions = [ { name: 'AzureAutomation(oiwmax001)' @@ -2008,7 +2030,6 @@ param tags = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } -param useResourcePermissions = true ``` @@ -2129,6 +2150,9 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = workspaceResourceId: '' } ] + features: { + enableLogAccessUsingOnlyResourcePermissions: true + } gallerySolutions: [ { name: 'AzureAutomation(oiwwaf001)' @@ -2173,7 +2197,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - useResourcePermissions: true } } ``` @@ -2300,6 +2323,11 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = } ] }, + "features": { + "value": { + "enableLogAccessUsingOnlyResourcePermissions": true + } + }, "gallerySolutions": { "value": [ { @@ -2361,9 +2389,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } - }, - "useResourcePermissions": { - "value": true } } } @@ -2481,6 +2506,9 @@ param diagnosticSettings = [ workspaceResourceId: '' } ] +param features = { + enableLogAccessUsingOnlyResourcePermissions: true +} param gallerySolutions = [ { name: 'AzureAutomation(oiwwaf001)' @@ -2525,7 +2553,6 @@ param tags = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } -param useResourcePermissions = true ``` @@ -2555,6 +2582,7 @@ param useResourcePermissions = true | [`dataSources`](#parameter-datasources) | array | LAW data sources to configure. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`features`](#parameter-features) | object | The workspace features. | | [`forceCmkForQuery`](#parameter-forcecmkforquery) | bool | Indicates whether customer managed storage is mandatory for query management. | | [`gallerySolutions`](#parameter-gallerysolutions) | array | List of gallerySolutions to be created in the log analytics workspace. | | [`linkedServices`](#parameter-linkedservices) | array | List of services to be linked. | @@ -2571,7 +2599,6 @@ param useResourcePermissions = true | [`storageInsightsConfigs`](#parameter-storageinsightsconfigs) | array | List of storage accounts to be read by the workspace. | | [`tables`](#parameter-tables) | array | LAW custom tables to be deployed. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`useResourcePermissions`](#parameter-useresourcepermissions) | bool | Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. | ### Parameter: `name` @@ -3078,6 +3105,60 @@ Enable/Disable usage telemetry for module. - MinValue: 0 - MaxValue: 730 +### Parameter: `features` + +The workspace features. + +- Required: No +- Type: object +- MinValue: 0 +- MaxValue: 730 + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`disableLocalAuth`](#parameter-featuresdisablelocalauth) | bool | Disable Non-EntraID based Auth. Default is true. | +| [`enableDataExport`](#parameter-featuresenabledataexport) | bool | Flag that indicate if data should be exported. | +| [`enableLogAccessUsingOnlyResourcePermissions`](#parameter-featuresenablelogaccessusingonlyresourcepermissions) | bool | Enable log access using only resource permissions. Default is false. | +| [`immediatePurgeDataOn30Days`](#parameter-featuresimmediatepurgedataon30days) | bool | Flag that describes if we want to remove the data after 30 days. | + +### Parameter: `features.disableLocalAuth` + +Disable Non-EntraID based Auth. Default is true. + +- Required: No +- Type: bool +- MinValue: 0 +- MaxValue: 730 + +### Parameter: `features.enableDataExport` + +Flag that indicate if data should be exported. + +- Required: No +- Type: bool +- MinValue: 0 +- MaxValue: 730 + +### Parameter: `features.enableLogAccessUsingOnlyResourcePermissions` + +Enable log access using only resource permissions. Default is false. + +- Required: No +- Type: bool +- MinValue: 0 +- MaxValue: 730 + +### Parameter: `features.immediatePurgeDataOn30Days` + +Flag that describes if we want to remove the data after 30 days. + +- Required: No +- Type: bool +- MinValue: 0 +- MaxValue: 730 + ### Parameter: `forceCmkForQuery` Indicates whether customer managed storage is mandatory for query management. @@ -4110,16 +4191,6 @@ Tags of the resource. - MinValue: 100 - MaxValue: 5000 -### Parameter: `useResourcePermissions` - -Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. - -- Required: No -- Type: bool -- Default: `False` -- MinValue: 100 -- MaxValue: 5000 - ## Outputs | Output | Type | Description | diff --git a/avm/res/operational-insights/workspace/data-export/README.md b/avm/res/operational-insights/workspace/data-export/README.md index c4bbb39e28..f6438994a9 100644 --- a/avm/res/operational-insights/workspace/data-export/README.md +++ b/avm/res/operational-insights/workspace/data-export/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Data Export. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/dataExports` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataExports) | +| `Microsoft.OperationalInsights/workspaces/dataExports` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/dataExports) | ## Parameters diff --git a/avm/res/operational-insights/workspace/data-export/main.bicep b/avm/res/operational-insights/workspace/data-export/main.bicep index a1e4649b51..9c0bfa7426 100644 --- a/avm/res/operational-insights/workspace/data-export/main.bicep +++ b/avm/res/operational-insights/workspace/data-export/main.bicep @@ -27,11 +27,11 @@ param tableNames string[] // Deployments // // =============== // -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: workspaceName } -resource dataExport 'Microsoft.OperationalInsights/workspaces/dataExports@2020-08-01' = { +resource dataExport 'Microsoft.OperationalInsights/workspaces/dataExports@2023-09-01' = { parent: workspace name: name properties: { diff --git a/avm/res/operational-insights/workspace/data-export/main.json b/avm/res/operational-insights/workspace/data-export/main.json index 6b96dce5d9..4b77c88859 100644 --- a/avm/res/operational-insights/workspace/data-export/main.json +++ b/avm/res/operational-insights/workspace/data-export/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14977926524140126314" + "version": "0.33.93.31351", + "templateHash": "15082251740316718256" }, "name": "Log Analytics Workspace Data Exports", "description": "This module deploys a Log Analytics Workspace Data Export." @@ -88,12 +88,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('workspaceName')]" }, "dataExport": { "type": "Microsoft.OperationalInsights/workspaces/dataExports", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "destination": "[parameters('destination')]", diff --git a/avm/res/operational-insights/workspace/data-source/README.md b/avm/res/operational-insights/workspace/data-source/README.md index 752f4583a9..5236dc5c9b 100644 --- a/avm/res/operational-insights/workspace/data-source/README.md +++ b/avm/res/operational-insights/workspace/data-source/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Data Source. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/dataSources) | ## Parameters diff --git a/avm/res/operational-insights/workspace/data-source/main.bicep b/avm/res/operational-insights/workspace/data-source/main.bicep index 138d596782..aa62d35059 100644 --- a/avm/res/operational-insights/workspace/data-source/main.bicep +++ b/avm/res/operational-insights/workspace/data-source/main.bicep @@ -56,11 +56,11 @@ param syslogName string? @description('Optional. Severities to configure when kind is LinuxSyslog.') param syslogSeverities array = [] -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: logAnalyticsWorkspaceName } -resource dataSource 'Microsoft.OperationalInsights/workspaces/dataSources@2020-08-01' = { +resource dataSource 'Microsoft.OperationalInsights/workspaces/dataSources@2023-09-01' = { name: name parent: workspace kind: kind diff --git a/avm/res/operational-insights/workspace/data-source/main.json b/avm/res/operational-insights/workspace/data-source/main.json index 4f5dd5b680..1a6b16e915 100644 --- a/avm/res/operational-insights/workspace/data-source/main.json +++ b/avm/res/operational-insights/workspace/data-source/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12082641043455308513" + "version": "0.33.93.31351", + "templateHash": "3186325409788999053" }, "name": "Log Analytics Workspace Datasources", "description": "This module deploys a Log Analytics Workspace Data Source." @@ -130,12 +130,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "dataSource": { "type": "Microsoft.OperationalInsights/workspaces/dataSources", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "kind": "[parameters('kind')]", "tags": "[parameters('tags')]", diff --git a/avm/res/operational-insights/workspace/linked-service/README.md b/avm/res/operational-insights/workspace/linked-service/README.md index bca53b7a24..75c3ecb002 100644 --- a/avm/res/operational-insights/workspace/linked-service/README.md +++ b/avm/res/operational-insights/workspace/linked-service/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Linked Service. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/linkedServices) | ## Parameters diff --git a/avm/res/operational-insights/workspace/linked-service/main.bicep b/avm/res/operational-insights/workspace/linked-service/main.bicep index 299750ed0a..4d2a2a86b7 100644 --- a/avm/res/operational-insights/workspace/linked-service/main.bicep +++ b/avm/res/operational-insights/workspace/linked-service/main.bicep @@ -16,11 +16,11 @@ param writeAccessResourceId string? @description('Optional. Tags to configure in the resource.') param tags object? -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: logAnalyticsWorkspaceName } -resource linkedService 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = { +resource linkedService 'Microsoft.OperationalInsights/workspaces/linkedServices@2023-09-01' = { name: name parent: workspace tags: tags diff --git a/avm/res/operational-insights/workspace/linked-service/main.json b/avm/res/operational-insights/workspace/linked-service/main.json index a7ece0d812..370cdfc4e9 100644 --- a/avm/res/operational-insights/workspace/linked-service/main.json +++ b/avm/res/operational-insights/workspace/linked-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17762270878383445421" + "version": "0.33.93.31351", + "templateHash": "15202444687633091947" }, "name": "Log Analytics Workspace Linked Services", "description": "This module deploys a Log Analytics Workspace Linked Service." @@ -50,12 +50,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "linkedService": { "type": "Microsoft.OperationalInsights/workspaces/linkedServices", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { diff --git a/avm/res/operational-insights/workspace/linked-storage-account/README.md b/avm/res/operational-insights/workspace/linked-storage-account/README.md index a4604fdeea..85e4ef7b54 100644 --- a/avm/res/operational-insights/workspace/linked-storage-account/README.md +++ b/avm/res/operational-insights/workspace/linked-storage-account/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Linked Storage Account. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/linkedStorageAccounts) | ## Parameters diff --git a/avm/res/operational-insights/workspace/linked-storage-account/main.bicep b/avm/res/operational-insights/workspace/linked-storage-account/main.bicep index e77ca26f23..5fc0c473ed 100644 --- a/avm/res/operational-insights/workspace/linked-storage-account/main.bicep +++ b/avm/res/operational-insights/workspace/linked-storage-account/main.bicep @@ -17,11 +17,11 @@ param name string @description('Required. Linked storage accounts resources Ids.') param storageAccountIds string[] -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: logAnalyticsWorkspaceName } -resource linkedStorageAccount 'Microsoft.OperationalInsights/workspaces/linkedStorageAccounts@2020-08-01' = { +resource linkedStorageAccount 'Microsoft.OperationalInsights/workspaces/linkedStorageAccounts@2023-09-01' = { name: name parent: workspace properties: { diff --git a/avm/res/operational-insights/workspace/linked-storage-account/main.json b/avm/res/operational-insights/workspace/linked-storage-account/main.json index 9ee99f5c48..603f7b3d2a 100644 --- a/avm/res/operational-insights/workspace/linked-storage-account/main.json +++ b/avm/res/operational-insights/workspace/linked-storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11986582912298301528" + "version": "0.33.93.31351", + "templateHash": "1489251440210164837" }, "name": "Log Analytics Workspace Linked Storage Accounts", "description": "This module deploys a Log Analytics Workspace Linked Storage Account." @@ -45,12 +45,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "linkedStorageAccount": { "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "properties": { "storageAccountIds": "[parameters('storageAccountIds')]" diff --git a/avm/res/operational-insights/workspace/main.bicep b/avm/res/operational-insights/workspace/main.bicep index edc8a326d2..d5b2e65c13 100644 --- a/avm/res/operational-insights/workspace/main.bicep +++ b/avm/res/operational-insights/workspace/main.bicep @@ -79,8 +79,8 @@ import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types @description('Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both.') param managedIdentities managedIdentityAllType? -@description('Optional. Set to \'true\' to use resource or workspace permissions and \'false\' (or leave empty) to require workspace permissions.') -param useResourcePermissions bool = false +@description('Optional. The workspace features.') +param features workspaceFeaturesType? @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingType[]? @@ -185,14 +185,17 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { location: location name: name tags: tags properties: { features: { searchVersion: 1 - enableLogAccessUsingOnlyResourcePermissions: useResourcePermissions + enableLogAccessUsingOnlyResourcePermissions: features.?enableLogAccessUsingOnlyResourcePermissions ?? false + disableLocalAuth: features.?disableLocalAuth ?? true + enableDataExport: features.?enableDataExport + immediatePurgeDataOn30Days: features.?immediatePurgeDataOn30Days } sku: { name: skuName @@ -648,3 +651,19 @@ type tableType = { @description('Optional. The role assignments for the table.') roleAssignments: roleAssignmentType[]? } + +@export() +@description('Features of the workspace.') +type workspaceFeaturesType = { + @description('Optional. Disable Non-EntraID based Auth. Default is true.') + disableLocalAuth: bool? + + @description('Optional. Flag that indicate if data should be exported.') + enableDataExport: bool? + + @description('Optional. Enable log access using only resource permissions. Default is false.') + enableLogAccessUsingOnlyResourcePermissions: bool? + + @description('Optional. Flag that describes if we want to remove the data after 30 days.') + immediatePurgeDataOn30Days: bool? +} diff --git a/avm/res/operational-insights/workspace/main.json b/avm/res/operational-insights/workspace/main.json index 1c6da556b6..4debca7e83 100644 --- a/avm/res/operational-insights/workspace/main.json +++ b/avm/res/operational-insights/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4169914284504175169" + "version": "0.33.93.31351", + "templateHash": "2355986442928910370" }, "name": "Log Analytics Workspaces", "description": "This module deploys a Log Analytics Workspace." @@ -523,6 +523,43 @@ "description": "Properties of the custom table." } }, + "workspaceFeaturesType": { + "type": "object", + "properties": { + "disableLocalAuth": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Disable Non-EntraID based Auth. Default is true." + } + }, + "enableDataExport": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Flag that indicate if data should be exported." + } + }, + "enableLogAccessUsingOnlyResourcePermissions": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable log access using only resource permissions. Default is false." + } + }, + "immediatePurgeDataOn30Days": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Flag that describes if we want to remove the data after 30 days." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Features of the workspace." + } + }, "_1.columnType": { "type": "object", "properties": { @@ -1070,11 +1107,11 @@ "description": "Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both." } }, - "useResourcePermissions": { - "type": "bool", - "defaultValue": false, + "features": { + "$ref": "#/definitions/workspaceFeaturesType", + "nullable": true, "metadata": { - "description": "Optional. Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions." + "description": "Optional. The workspace features." } }, "diagnosticSettings": { @@ -1173,14 +1210,17 @@ }, "logAnalyticsWorkspace": { "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { "features": { "searchVersion": 1, - "enableLogAccessUsingOnlyResourcePermissions": "[parameters('useResourcePermissions')]" + "enableLogAccessUsingOnlyResourcePermissions": "[coalesce(tryGet(parameters('features'), 'enableLogAccessUsingOnlyResourcePermissions'), false())]", + "disableLocalAuth": "[coalesce(tryGet(parameters('features'), 'disableLocalAuth'), true())]", + "enableDataExport": "[tryGet(parameters('features'), 'enableDataExport')]", + "immediatePurgeDataOn30Days": "[tryGet(parameters('features'), 'immediatePurgeDataOn30Days')]" }, "sku": { "name": "[parameters('skuName')]", @@ -1318,8 +1358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "12715459455039038966" + "version": "0.33.93.31351", + "templateHash": "14024094214852981332" }, "name": "Log Analytics Workspace Storage Insight Configs", "description": "This module deploys a Log Analytics Workspace Storage Insight Config." @@ -1382,12 +1422,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "storageinsightconfig": { "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { @@ -1463,8 +1503,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "17369295448584741847" + "version": "0.33.93.31351", + "templateHash": "15202444687633091947" }, "name": "Log Analytics Workspace Linked Services", "description": "This module deploys a Log Analytics Workspace Linked Service." @@ -1508,12 +1548,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "linkedService": { "type": "Microsoft.OperationalInsights/workspaces/linkedServices", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { @@ -1582,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4336014228195179628" + "version": "0.33.93.31351", + "templateHash": "1489251440210164837" }, "name": "Log Analytics Workspace Linked Storage Accounts", "description": "This module deploys a Log Analytics Workspace Linked Storage Account." @@ -1622,12 +1662,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "linkedStorageAccount": { "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "properties": { "storageAccountIds": "[parameters('storageAccountIds')]" @@ -1715,8 +1755,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "10687105232425640835" + "version": "0.33.93.31351", + "templateHash": "14407663387414945082" }, "name": "Log Analytics Workspace Saved Searches", "description": "This module deploys a Log Analytics Workspace Saved Search." @@ -1792,12 +1832,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "savedSearch": { "type": "Microsoft.OperationalInsights/workspaces/savedSearches", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "properties": { "etag": "[parameters('etag')]", @@ -1878,8 +1918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9265911316713795008" + "version": "0.33.93.31351", + "templateHash": "15082251740316718256" }, "name": "Log Analytics Workspace Data Exports", "description": "This module deploys a Log Analytics Workspace Data Export." @@ -1961,12 +2001,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('workspaceName')]" }, "dataExport": { "type": "Microsoft.OperationalInsights/workspaces/dataExports", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "destination": "[parameters('destination')]", @@ -2071,8 +2111,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "16545899498885737893" + "version": "0.33.93.31351", + "templateHash": "3186325409788999053" }, "name": "Log Analytics Workspace Datasources", "description": "This module deploys a Log Analytics Workspace Data Source." @@ -2196,12 +2236,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "dataSource": { "type": "Microsoft.OperationalInsights/workspaces/dataSources", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "kind": "[parameters('kind')]", "tags": "[parameters('tags')]", @@ -2298,8 +2338,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9728326644814973167" + "version": "0.33.93.31351", + "templateHash": "4329017759962267155" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table." @@ -2648,12 +2688,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('workspaceName')]" }, "table": { "type": "Microsoft.OperationalInsights/workspaces/tables", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "plan": "[parameters('plan')]", @@ -2942,7 +2982,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('logAnalyticsWorkspace', '2022-10-01', 'full').location]" + "value": "[reference('logAnalyticsWorkspace', '2023-09-01', 'full').location]" }, "systemAssignedMIPrincipalId": { "type": "string", @@ -2950,7 +2990,7 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('logAnalyticsWorkspace', '2022-10-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('logAnalyticsWorkspace', '2023-09-01', 'full'), 'identity'), 'principalId')]" } } } \ No newline at end of file diff --git a/avm/res/operational-insights/workspace/saved-search/README.md b/avm/res/operational-insights/workspace/saved-search/README.md index dfd853b645..697d8e95b1 100644 --- a/avm/res/operational-insights/workspace/saved-search/README.md +++ b/avm/res/operational-insights/workspace/saved-search/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Saved Search. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/savedSearches) | ## Parameters diff --git a/avm/res/operational-insights/workspace/saved-search/main.bicep b/avm/res/operational-insights/workspace/saved-search/main.bicep index e0b9512ed2..00a2de1f29 100644 --- a/avm/res/operational-insights/workspace/saved-search/main.bicep +++ b/avm/res/operational-insights/workspace/saved-search/main.bicep @@ -31,11 +31,11 @@ param version int? @description('Optional. The ETag of the saved search. To override an existing saved search, use "*" or specify the current Etag.') param etag string = '*' -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: logAnalyticsWorkspaceName } -resource savedSearch 'Microsoft.OperationalInsights/workspaces/savedSearches@2020-08-01' = { +resource savedSearch 'Microsoft.OperationalInsights/workspaces/savedSearches@2023-09-01' = { name: name parent: workspace //etag: etag // According to API, the variable should be here, but it doesn't work here. diff --git a/avm/res/operational-insights/workspace/saved-search/main.json b/avm/res/operational-insights/workspace/saved-search/main.json index 34553f9fe9..8d1d1cae6c 100644 --- a/avm/res/operational-insights/workspace/saved-search/main.json +++ b/avm/res/operational-insights/workspace/saved-search/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9640551980154944642" + "version": "0.33.93.31351", + "templateHash": "14407663387414945082" }, "name": "Log Analytics Workspace Saved Searches", "description": "This module deploys a Log Analytics Workspace Saved Search." @@ -82,12 +82,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "savedSearch": { "type": "Microsoft.OperationalInsights/workspaces/savedSearches", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "properties": { "etag": "[parameters('etag')]", diff --git a/avm/res/operational-insights/workspace/storage-insight-config/README.md b/avm/res/operational-insights/workspace/storage-insight-config/README.md index 8bd800b2d2..34afc44426 100644 --- a/avm/res/operational-insights/workspace/storage-insight-config/README.md +++ b/avm/res/operational-insights/workspace/storage-insight-config/README.md @@ -12,7 +12,7 @@ This module deploys a Log Analytics Workspace Storage Insight Config. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/storageInsightConfigs) | ## Parameters diff --git a/avm/res/operational-insights/workspace/storage-insight-config/main.bicep b/avm/res/operational-insights/workspace/storage-insight-config/main.bicep index 4ec9de9a68..8cb9aabb2d 100644 --- a/avm/res/operational-insights/workspace/storage-insight-config/main.bicep +++ b/avm/res/operational-insights/workspace/storage-insight-config/main.bicep @@ -23,11 +23,11 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing name: last(split(storageAccountResourceId, '/'))! } -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: logAnalyticsWorkspaceName } -resource storageinsightconfig 'Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2020-08-01' = { +resource storageinsightconfig 'Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2023-09-01' = { name: name parent: workspace tags: tags diff --git a/avm/res/operational-insights/workspace/storage-insight-config/main.json b/avm/res/operational-insights/workspace/storage-insight-config/main.json index 15f3aed89c..e80b09bf6b 100644 --- a/avm/res/operational-insights/workspace/storage-insight-config/main.json +++ b/avm/res/operational-insights/workspace/storage-insight-config/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "12148501849006788324" + "version": "0.33.93.31351", + "templateHash": "14024094214852981332" }, "name": "Log Analytics Workspace Storage Insight Configs", "description": "This module deploys a Log Analytics Workspace Storage Insight Config." @@ -69,12 +69,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('logAnalyticsWorkspaceName')]" }, "storageinsightconfig": { "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs", - "apiVersion": "2020-08-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { diff --git a/avm/res/operational-insights/workspace/table/README.md b/avm/res/operational-insights/workspace/table/README.md index c3cbc41a4c..32a0f782c8 100644 --- a/avm/res/operational-insights/workspace/table/README.md +++ b/avm/res/operational-insights/workspace/table/README.md @@ -14,7 +14,7 @@ This module deploys a Log Analytics Workspace Table. | 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.OperationalInsights/workspaces/tables` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces/tables) | +| `Microsoft.OperationalInsights/workspaces/tables` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces/tables) | ## Parameters diff --git a/avm/res/operational-insights/workspace/table/main.bicep b/avm/res/operational-insights/workspace/table/main.bicep index ed15f51109..a886ffc8f3 100644 --- a/avm/res/operational-insights/workspace/table/main.bicep +++ b/avm/res/operational-insights/workspace/table/main.bicep @@ -86,11 +86,11 @@ var formattedRoleAssignments = [ // Deployments // // =============== // -resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { +resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { name: workspaceName } -resource table 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' = { +resource table 'Microsoft.OperationalInsights/workspaces/tables@2023-09-01' = { parent: workspace name: name properties: { diff --git a/avm/res/operational-insights/workspace/table/main.json b/avm/res/operational-insights/workspace/table/main.json index fa7999f6b9..fdf48788fd 100644 --- a/avm/res/operational-insights/workspace/table/main.json +++ b/avm/res/operational-insights/workspace/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8047367069034259642" + "version": "0.33.93.31351", + "templateHash": "4329017759962267155" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table." @@ -355,12 +355,12 @@ "workspace": { "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[parameters('workspaceName')]" }, "table": { "type": "Microsoft.OperationalInsights/workspaces/tables", - "apiVersion": "2022-10-01", + "apiVersion": "2023-09-01", "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "plan": "[parameters('plan')]", diff --git a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep index 3a052d4bae..b16ae0b3b5 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep @@ -227,7 +227,9 @@ module testDeployment '../../../main.bicep' = [ ] } ] - useResourcePermissions: true + features: { + enableLogAccessUsingOnlyResourcePermissions: true + } tables: [ { name: 'CustomTableBasic_CL' diff --git a/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep index 9b069e5ef2..e8f0c15a3e 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep @@ -173,6 +173,12 @@ module testDeployment '../../../main.bicep' = [ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } ] + features: { + enableLogAccessUsingOnlyResourcePermissions: true + disableLocalAuth: true + enableDataExport: true + immediatePurgeDataOn30Days: true + } gallerySolutions: [ { name: 'AzureAutomation(${namePrefix}${serviceShort}001)' @@ -338,7 +344,6 @@ module testDeployment '../../../main.bicep' = [ ] } ] - useResourcePermissions: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -370,9 +375,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep index cf61ff31a9..5fa264c253 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -201,7 +201,9 @@ module testDeployment '../../../main.bicep' = [ ] } ] - useResourcePermissions: true + features: { + enableLogAccessUsingOnlyResourcePermissions: true + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -211,9 +213,5 @@ module testDeployment '../../../main.bicep' = [ systemAssigned: true } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/operational-insights/workspace/version.json b/avm/res/operational-insights/workspace/version.json index a830c3d961..91ffa760bf 100644 --- a/avm/res/operational-insights/workspace/version.json +++ b/avm/res/operational-insights/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.10", + "version": "0.11", "pathFilters": [ "./main.json" ] From f84487a64d2805af4516297e1597f81b2c73a996 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 11 Feb 2025 11:09:38 +0100 Subject: [PATCH 19/53] feat: VPN-Site - Updated to latest UDTs (#4336) ## Description Updated to latest UDTs ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.vpn-site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml/badge.svg?branch=users%2Falsehr%2FvpnSite&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/network/vpn-site/README.md | 28 +--- avm/res/network/vpn-site/main.bicep | 44 +---- avm/res/network/vpn-site/main.json | 156 ++++++++++-------- .../tests/e2e/defaults/main.test.bicep | 4 - .../vpn-site/tests/e2e/max/main.test.bicep | 3 - .../tests/e2e/waf-aligned/main.test.bicep | 7 - 6 files changed, 97 insertions(+), 145 deletions(-) diff --git a/avm/res/network/vpn-site/README.md b/avm/res/network/vpn-site/README.md index 92f162d8a7..6d3a7c3542 100644 --- a/avm/res/network/vpn-site/README.md +++ b/avm/res/network/vpn-site/README.md @@ -8,6 +8,7 @@ This module deploys a VPN Site. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) - [Notes](#Notes) - [Data Collection](#Data-Collection) @@ -52,7 +53,6 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { '10.0.0.0/16' ] ipAddress: '1.2.3.4' - location: '' } } ``` @@ -84,9 +84,6 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { }, "ipAddress": { "value": "1.2.3.4" - }, - "location": { - "value": "" } } } @@ -110,7 +107,6 @@ param addressPrefixes = [ '10.0.0.0/16' ] param ipAddress = '1.2.3.4' -param location = '' ``` @@ -422,10 +418,6 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { linkSpeedInMbps: 0 } location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } o365Policy: { breakOutCategories: { allow: true @@ -500,12 +492,6 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { "location": { "value": "" }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "o365Policy": { "value": { "breakOutCategories": { @@ -576,10 +562,6 @@ param deviceProperties = { linkSpeedInMbps: 0 } param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} o365Policy: { breakOutCategories: { allow: true @@ -900,6 +882,14 @@ List of all VPN site links. | `resourceGroupName` | string | The resource group the VPN site was deployed into. | | `resourceId` | string | The resource ID of the VPN site. | +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + ## Notes ### Parameter Usage `deviceProperties` diff --git a/avm/res/network/vpn-site/main.bicep b/avm/res/network/vpn-site/main.bicep index a203cf4305..3e2cfa9d5b 100644 --- a/avm/res/network/vpn-site/main.bicep +++ b/avm/res/network/vpn-site/main.bicep @@ -37,11 +37,13 @@ param enableTelemetry bool = true @description('Optional. List of all VPN site links.') param vpnSiteLinks array = [] +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') @@ -154,41 +156,3 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = vpnSite.location - -// =============== // -// Definitions // -// =============== // - -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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/vpn-site/main.json b/avm/res/network/vpn-site/main.json index af1125d3f1..bff519c79d 100644 --- a/avm/res/network/vpn-site/main.json +++ b/avm/res/network/vpn-site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11509955796069504943" + "version": "0.33.13.18514", + "templateHash": "18110577874087174381" }, "name": "VPN Sites", "description": "This module deploys a VPN Site." @@ -35,80 +35,87 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -196,12 +203,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } diff --git a/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep index 4818c6fb53..7a2b075e26 100644 --- a/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep +++ b/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep @@ -50,7 +50,6 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - location: resourceLocation name: '${namePrefix}-${serviceShort}' virtualWanId: nestedDependencies.outputs.virtualWWANResourceId addressPrefixes: [ @@ -58,8 +57,5 @@ module testDeployment '../../../main.bicep' = [ ] ipAddress: '1.2.3.4' } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep index c3969e70e5..00e33360e0 100644 --- a/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep +++ b/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep @@ -126,8 +126,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep index 5f582100f2..36d8fa73cd 100644 --- a/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep @@ -54,10 +54,6 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation name: '${namePrefix}-${serviceShort}' virtualWanId: nestedDependencies.outputs.virtualWWANResourceId - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } tags: { 'hidden-title': 'This is visible in the resource name' tagA: 'valueA' @@ -104,8 +100,5 @@ module testDeployment '../../../main.bicep' = [ } } } - dependsOn: [ - nestedDependencies - ] } ] From 14d87e8411f88604cd92b82d5ae7f808b188089a Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 11 Feb 2025 11:12:38 +0100 Subject: [PATCH 20/53] feat: VPN-GW - Update to latest UDTs (#4338) ## Description Update to latest UDTs ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.vpn-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml/badge.svg?branch=users%2Falsehr%2FvpnGWUDT&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/network/vpn-gateway/README.md | 31 ++++++------------ avm/res/network/vpn-gateway/main.bicep | 21 +++--------- avm/res/network/vpn-gateway/main.json | 32 +++++++++++++------ .../network/vpn-gateway/nat-rule/main.json | 4 +-- .../tests/e2e/defaults/main.test.bicep | 4 --- .../vpn-gateway/tests/e2e/max/main.test.bicep | 3 -- .../tests/e2e/waf-aligned/main.test.bicep | 7 ---- .../vpn-gateway/vpn-connection/main.json | 4 +-- 8 files changed, 40 insertions(+), 66 deletions(-) diff --git a/avm/res/network/vpn-gateway/README.md b/avm/res/network/vpn-gateway/README.md index 26eec74c1c..5bc27b3de4 100644 --- a/avm/res/network/vpn-gateway/README.md +++ b/avm/res/network/vpn-gateway/README.md @@ -8,6 +8,7 @@ This module deploys a VPN Gateway. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) - [Notes](#Notes) - [Data Collection](#Data-Collection) @@ -48,8 +49,6 @@ module vpnGateway 'br/public:avm/res/network/vpn-gateway:' = { // Required parameters name: 'vpngmin001' virtualHubResourceId: '' - // Non-required parameters - location: '' } } ``` @@ -72,10 +71,6 @@ module vpnGateway 'br/public:avm/res/network/vpn-gateway:' = { }, "virtualHubResourceId": { "value": "" - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -94,8 +89,6 @@ using 'br/public:avm/res/network/vpn-gateway:' // Required parameters param name = 'vpngmin001' param virtualHubResourceId = '' -// Non-required parameters -param location = '' ``` @@ -334,10 +327,6 @@ module vpnGateway 'br/public:avm/res/network/vpn-gateway:' = { peerWeight: 0 } location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } natRules: [ { externalMappings: [ @@ -407,12 +396,6 @@ module vpnGateway 'br/public:avm/res/network/vpn-gateway:' = { "location": { "value": "" }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "natRules": { "value": [ { @@ -478,10 +461,6 @@ param bgpSettings = { peerWeight: 0 } param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} param natRules = [ { externalMappings: [ @@ -677,6 +656,14 @@ The scale unit for this VPN gateway. | `resourceGroupName` | string | The name of the resource group the VPN gateway was deployed into. | | `resourceId` | string | The resource ID of the VPN gateway. | +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + ## Notes ### Parameter Usage: `bgpSettings` diff --git a/avm/res/network/vpn-gateway/main.bicep b/avm/res/network/vpn-gateway/main.bicep index 35b521cb72..763c83c387 100644 --- a/avm/res/network/vpn-gateway/main.bicep +++ b/avm/res/network/vpn-gateway/main.bicep @@ -31,8 +31,9 @@ param vpnGatewayScaleUnit int = 2 @description('Optional. Tags of the resource.') param tags object? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true @@ -116,9 +117,9 @@ module vpnGateway_natRules 'nat-rule/main.bicep' = [ params: { name: natRule.name vpnGatewayName: vpnGateway.name - externalMappings: contains(natRule, 'externalMappings') ? natRule.externalMappings : [] - internalMappings: contains(natRule, 'internalMappings') ? natRule.internalMappings : [] - ipConfigurationId: contains(natRule, 'ipConfigurationId') ? natRule.ipConfigurationId : '' + externalMappings: natRule.?externalMappings + internalMappings: natRule.?internalMappings + ipConfigurationId: natRule.?ipConfigurationId mode: natRule.?mode type: natRule.?type } @@ -160,15 +161,3 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = vpnGateway.location - -// =============== // -// Definitions // -// =============== // - -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? diff --git a/avm/res/network/vpn-gateway/main.json b/avm/res/network/vpn-gateway/main.json index 11f4bc8d97..1a963a7743 100644 --- a/avm/res/network/vpn-gateway/main.json +++ b/avm/res/network/vpn-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8342635055821923062" + "version": "0.33.13.18514", + "templateHash": "13461484235913765557" }, "name": "VPN Gateways", "description": "This module deploys a VPN Gateway." @@ -35,7 +35,12 @@ } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -109,6 +114,7 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -217,9 +223,15 @@ "vpnGatewayName": { "value": "[parameters('name')]" }, - "externalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'externalMappings'), createObject('value', parameters('natRules')[copyIndex()].externalMappings), createObject('value', createArray()))]", - "internalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'internalMappings'), createObject('value', parameters('natRules')[copyIndex()].internalMappings), createObject('value', createArray()))]", - "ipConfigurationId": "[if(contains(parameters('natRules')[copyIndex()], 'ipConfigurationId'), createObject('value', parameters('natRules')[copyIndex()].ipConfigurationId), createObject('value', ''))]", + "externalMappings": { + "value": "[tryGet(parameters('natRules')[copyIndex()], 'externalMappings')]" + }, + "internalMappings": { + "value": "[tryGet(parameters('natRules')[copyIndex()], 'internalMappings')]" + }, + "ipConfigurationId": { + "value": "[tryGet(parameters('natRules')[copyIndex()], 'ipConfigurationId')]" + }, "mode": { "value": "[tryGet(parameters('natRules')[copyIndex()], 'mode')]" }, @@ -234,8 +246,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1228645763554247410" + "version": "0.33.13.18514", + "templateHash": "11380942489395706225" }, "name": "VPN Gateway NAT Rules", "description": "This module deploys a VPN Gateway NAT Rule." @@ -415,8 +427,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5835591601144574416" + "version": "0.33.13.18514", + "templateHash": "6343342170986309278" }, "name": "VPN Gateway VPN Connections", "description": "This module deploys a VPN Gateway VPN Connection." diff --git a/avm/res/network/vpn-gateway/nat-rule/main.json b/avm/res/network/vpn-gateway/nat-rule/main.json index 4cc344b05e..dd5e884bea 100644 --- a/avm/res/network/vpn-gateway/nat-rule/main.json +++ b/avm/res/network/vpn-gateway/nat-rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1228645763554247410" + "version": "0.33.13.18514", + "templateHash": "11380942489395706225" }, "name": "VPN Gateway NAT Rules", "description": "This module deploys a VPN Gateway NAT Rule." diff --git a/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep index 8debc2b069..1106c5eabd 100644 --- a/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep +++ b/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep @@ -51,12 +51,8 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - location: resourceLocation name: '${namePrefix}${serviceShort}001' virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep index b51c76ee82..5f0696cb07 100644 --- a/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep @@ -99,8 +99,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep index 5213b39d56..db69482e5f 100644 --- a/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep @@ -72,10 +72,6 @@ module testDeployment '../../../main.bicep' = [ routingWeight: 0 } ] - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } natRules: [ { externalMappings: [ @@ -99,8 +95,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/vpn-gateway/vpn-connection/main.json b/avm/res/network/vpn-gateway/vpn-connection/main.json index d0a68d2f0c..f345f8e837 100644 --- a/avm/res/network/vpn-gateway/vpn-connection/main.json +++ b/avm/res/network/vpn-gateway/vpn-connection/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5835591601144574416" + "version": "0.33.13.18514", + "templateHash": "6343342170986309278" }, "name": "VPN Gateway VPN Connections", "description": "This module deploys a VPN Gateway VPN Connection." From 8e313e132ce2673339dc92ef35909c7843f9b420 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 11 Feb 2025 11:23:31 +0100 Subject: [PATCH 21/53] feat: PowerBI - Update to latest UDT (#4307) ## Description Update to latest UDT ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.power-bi-dedicated.capacity](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml/badge.svg?branch=users%2Falsehr%2Fpowerbi&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/power-bi-dedicated/capacity/README.md | 46 ++-- .../power-bi-dedicated/capacity/main.bicep | 60 ++--- avm/res/power-bi-dedicated/capacity/main.json | 216 ++++++++++-------- .../tests/e2e/defaults/main.test.bicep | 1 - .../tests/e2e/waf-aligned/main.test.bicep | 5 - .../power-bi-dedicated/capacity/version.json | 4 +- 6 files changed, 160 insertions(+), 172 deletions(-) diff --git a/avm/res/power-bi-dedicated/capacity/README.md b/avm/res/power-bi-dedicated/capacity/README.md index c2f1039aab..3ea5083542 100644 --- a/avm/res/power-bi-dedicated/capacity/README.md +++ b/avm/res/power-bi-dedicated/capacity/README.md @@ -8,6 +8,7 @@ This module deploys a Power BI Dedicated Capacity. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) - [Data Collection](#Data-Collection) ## Resource Types @@ -51,8 +52,6 @@ module capacity 'br/public:avm/res/power-bi-dedicated/capacity:' = { sku: { capacity: 1 } - // Non-required parameters - location: '' } } ``` @@ -82,10 +81,6 @@ module capacity 'br/public:avm/res/power-bi-dedicated/capacity:' = { "value": { "capacity": 1 } - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -109,8 +104,6 @@ param name = 'pbdcapmin001' param sku = { capacity: 1 } -// Non-required parameters -param location = '' ``` @@ -319,11 +312,6 @@ module capacity 'br/public:avm/res/power-bi-dedicated/capacity:' = { capacity: 1 } // Non-required parameters - location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -360,15 +348,6 @@ module capacity 'br/public:avm/res/power-bi-dedicated/capacity:' = { } }, // Non-required parameters - "location": { - "value": "" - }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "tags": { "value": { "Environment": "Non-Prod", @@ -399,11 +378,6 @@ param sku = { capacity: 1 } // Non-required parameters -param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} param tags = { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -588,7 +562,7 @@ Array of role assignments to create. - `'Log Analytics Reader'` - `'Owner'` - `'Reader'` - - `'Role Based Access Control Administrator (Preview)'` + - `'Role Based Access Control Administrator'` - `'User Access Administrator'` **Required parameters** @@ -606,6 +580,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -656,6 +631,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -689,6 +671,14 @@ Tags of the resource. | `resourceGroupName` | string | The name of the resource group the PowerBi Embedded was created in. | | `resourceId` | string | The resource ID of the PowerBi Embedded instance. | +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/power-bi-dedicated/capacity/main.bicep b/avm/res/power-bi-dedicated/capacity/main.bicep index 8b120ea109..dc803bc979 100644 --- a/avm/res/power-bi-dedicated/capacity/main.bicep +++ b/avm/res/power-bi-dedicated/capacity/main.bicep @@ -26,11 +26,13 @@ param members array @description('Optional. Mode of the resource.') param mode string = 'Gen2' +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') @@ -44,7 +46,7 @@ var builtInRoleNames = { ) 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( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) @@ -54,6 +56,17 @@ var builtInRoleNames = { ) } +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.powerbidedicated-capacity.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -102,14 +115,10 @@ resource capacity_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(l } resource capacity_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(capacity.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(capacity.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -137,37 +146,6 @@ output location string = capacity.location // Definitions // // =============== // -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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 skuTpe = { @description('Optional. The name of the SKU.') name: ('A1' | 'A2' | 'A3' | 'A4' | 'A5' | 'A6')? diff --git a/avm/res/power-bi-dedicated/capacity/main.json b/avm/res/power-bi-dedicated/capacity/main.json index 25a23c68e6..5fd245d210 100644 --- a/avm/res/power-bi-dedicated/capacity/main.json +++ b/avm/res/power-bi-dedicated/capacity/main.json @@ -5,13 +5,51 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "11211810764868342264" + "version": "0.33.13.18514", + "templateHash": "16622551566318541504" }, "name": "Power BI Dedicated Capacities", "description": "This module deploys a Power BI Dedicated Capacity." }, "definitions": { + "skuTpe": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "A1", + "A2", + "A3", + "A4", + "A5", + "A6" + ], + "nullable": true, + "metadata": { + "description": "Optional. The name of the SKU." + } + }, + "capacity": { + "type": "int", + "metadata": { + "description": "Required. The capacity of the SKU." + } + }, + "tier": { + "type": "string", + "allowedValues": [ + "AutoPremiumHost", + "PBIE_Azure", + "Premium" + ], + "nullable": true, + "metadata": { + "description": "Optional. The tier of the SKU." + } + } + } + }, "lockType": { "type": "object", "properties": { @@ -35,110 +73,86 @@ } } }, - "nullable": true - }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "skuTpe": { + "roleAssignmentType": { "type": "object", "properties": { "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { "type": "string", "allowedValues": [ - "A1", - "A2", - "A3", - "A4", - "A5", - "A6" + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" ], "nullable": true, "metadata": { - "description": "Optional. The name of the SKU." + "description": "Optional. The principal type of the assigned principal ID." } }, - "capacity": { - "type": "int", + "description": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The capacity of the SKU." + "description": "Optional. The description of the role assignment." } }, - "tier": { + "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": [ - "AutoPremiumHost", - "PBIE_Azure", - "Premium" + "2.0" ], "nullable": true, "metadata": { - "description": "Optional. The tier of the SKU." + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." } } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } } }, @@ -195,25 +209,37 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", "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')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -273,20 +299,20 @@ "capacity_roleAssignments": { "copy": { "name": "capacity_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.PowerBIDedicated/capacities/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.PowerBIDedicated/capacities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.PowerBIDedicated/capacities', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "capacity" diff --git a/avm/res/power-bi-dedicated/capacity/tests/e2e/defaults/main.test.bicep b/avm/res/power-bi-dedicated/capacity/tests/e2e/defaults/main.test.bicep index 515de7d0b9..6507af13cf 100644 --- a/avm/res/power-bi-dedicated/capacity/tests/e2e/defaults/main.test.bicep +++ b/avm/res/power-bi-dedicated/capacity/tests/e2e/defaults/main.test.bicep @@ -57,7 +57,6 @@ module testDeployment '../../../main.bicep' = [ members: [ nestedDependencies.outputs.managedIdentityPrincipalId ] - location: resourceLocation } } ] diff --git a/avm/res/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep b/avm/res/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep index 06d353ab48..89fe36f4c7 100644 --- a/avm/res/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/power-bi-dedicated/capacity/tests/e2e/waf-aligned/main.test.bicep @@ -51,14 +51,9 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation sku: { capacity: 1 } - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } members: [ nestedDependencies.outputs.managedIdentityPrincipalId ] diff --git a/avm/res/power-bi-dedicated/capacity/version.json b/avm/res/power-bi-dedicated/capacity/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/power-bi-dedicated/capacity/version.json +++ b/avm/res/power-bi-dedicated/capacity/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file From 643c812142b11fd1c8654a1ebc0d3c20401da7e5 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Wed, 12 Feb 2025 00:19:24 +0100 Subject: [PATCH 22/53] feat: Container-App - Removed workaround for secrets (#4426) ## Description Replaced workaround of `secureList` with UDT ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.app.container-app](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=users%2Falsehr%2FdeprecateSecureListContainerApp&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml) | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 --- avm/res/app/container-app/README.md | 156 +++++++++--------- avm/res/app/container-app/main.bicep | 50 ++++-- avm/res/app/container-app/main.json | 49 +++++- .../tests/e2e/defaults/main.test.bicep | 4 - .../tests/e2e/disable-ingress/main.test.bicep | 1 - .../tests/e2e/max/main.test.bicep | 26 ++- .../tests/e2e/vnet/main.test.bicep | 1 - .../tests/e2e/waf-aligned/main.test.bicep | 8 - avm/res/app/container-app/version.json | 2 +- 9 files changed, 168 insertions(+), 129 deletions(-) diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md index 8fb2b89697..e07c1f8f48 100644 --- a/avm/res/app/container-app/README.md +++ b/avm/res/app/container-app/README.md @@ -59,8 +59,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ] environmentResourceId: '' name: 'acamin001' - // Non-required parameters - location: '' } } ``` @@ -95,10 +93,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { }, "name": { "value": "acamin001" - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -127,8 +121,6 @@ param containers = [ ] param environmentResourceId = '' param name = 'acamin001' -// Non-required parameters -param location = '' ``` @@ -162,7 +154,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { name: 'acapriv001' // Non-required parameters disableIngress: true - location: '' } } ``` @@ -201,9 +192,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { // Non-required parameters "disableIngress": { "value": true - }, - "location": { - "value": "" } } } @@ -234,7 +222,6 @@ param environmentResourceId = '' param name = 'acapriv001' // Non-required parameters param disableIngress = true -param location = '' ``` @@ -335,19 +322,17 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ] } } - secrets: { - secureList: [ - { - name: 'containerappstoredsecret' - value: '' - } - { - identity: '' - keyVaultUrl: '' - name: 'keyvaultstoredsecret' - } - ] - } + secrets: [ + { + name: 'containerappstoredsecret' + value: '' + } + { + identity: '' + keyVaultUrl: '' + name: 'keyvaultstoredsecret' + } + ] tags: { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -467,19 +452,17 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } }, "secrets": { - "value": { - "secureList": [ - { - "name": "containerappstoredsecret", - "value": "" - }, - { - "identity": "", - "keyVaultUrl": "", - "name": "keyvaultstoredsecret" - } - ] - } + "value": [ + { + "name": "containerappstoredsecret", + "value": "" + }, + { + "identity": "", + "keyVaultUrl": "", + "name": "keyvaultstoredsecret" + } + ] }, "tags": { "value": { @@ -583,19 +566,17 @@ param runtime = { ] } } -param secrets = { - secureList: [ - { - name: 'containerappstoredsecret' - value: '' - } - { - identity: '' - keyVaultUrl: '' - name: 'keyvaultstoredsecret' - } - ] -} +param secrets = [ + { + name: 'containerappstoredsecret' + value: '' + } + { + identity: '' + keyVaultUrl: '' + name: 'keyvaultstoredsecret' + } +] param tags = { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -643,7 +624,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ingressExternal: false ingressTargetPort: 80 ingressTransport: 'tcp' - location: '' } } ``` @@ -700,9 +680,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { }, "ingressTransport": { "value": "tcp" - }, - "location": { - "value": "" } } } @@ -743,7 +720,6 @@ param ingressAllowInsecure = false param ingressExternal = false param ingressTargetPort = 80 param ingressTransport = 'tcp' -param location = '' ``` @@ -795,11 +771,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { // Non-required parameters ingressAllowInsecure: false ingressExternal: false - location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } managedIdentities: { userAssignedResourceIds: [ '' @@ -868,15 +839,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "ingressExternal": { "value": false }, - "location": { - "value": "" - }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "managedIdentities": { "value": { "userAssignedResourceIds": [ @@ -937,11 +899,6 @@ param name = 'acawaf001' // Non-required parameters param ingressAllowInsecure = false param ingressExternal = false -param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} param managedIdentities = { userAssignedResourceIds: [ '' @@ -997,7 +954,7 @@ param tags = { | [`scaleMaxReplicas`](#parameter-scalemaxreplicas) | int | Maximum number of container replicas. Defaults to 10 if not set. | | [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. Defaults to 3 if not set. | | [`scaleRules`](#parameter-scalerules) | array | Scaling rules. | -| [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. | +| [`secrets`](#parameter-secrets) | array | The secrets of the Container App. | | [`service`](#parameter-service) | object | Dev ContainerApp service type. | | [`serviceBinds`](#parameter-servicebinds) | array | List of container app services bound to the app. | | [`stickySessionsAffinity`](#parameter-stickysessionsaffinity) | string | Bool indicating if the Container App should enable session affinity. | @@ -1970,8 +1927,49 @@ Scaling rules. The secrets of the Container App. - Required: No -- Type: secureObject -- Default: `{}` +- Type: array + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultUrl`](#parameter-secretskeyvaulturl) | string | Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null. | +| [`value`](#parameter-secretsvalue) | securestring | The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`identity`](#parameter-secretsidentity) | string | Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity. | +| [`name`](#parameter-secretsname) | string | The name of the secret. | + +### Parameter: `secrets.keyVaultUrl` + +Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null. + +- Required: No +- Type: string + +### Parameter: `secrets.value` + +The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null. + +- Required: No +- Type: securestring + +### Parameter: `secrets.identity` + +Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity. + +- Required: No +- Type: string + +### Parameter: `secrets.name` + +The name of the secret. + +- Required: No +- Type: string ### Parameter: `service` diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep index 201ba8da5b..3f6e2fcd83 100644 --- a/avm/res/app/container-app/main.bicep +++ b/avm/res/app/container-app/main.bicep @@ -135,8 +135,7 @@ param containers containerType[] param initContainersTemplate array = [] @description('Optional. The secrets of the Container App.') -@secure() -param secrets object = {} +param secrets secretType[]? @description('Optional. User friendly suffix that is appended to the revision name.') param revisionSuffix string = '' @@ -147,8 +146,6 @@ param volumes array = [] @description('Optional. Workload profile name to pin for container app execution.') param workloadProfileName string = '' -var secretList = !empty(secrets) ? secrets.secureList : [] - var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -261,20 +258,24 @@ resource containerApp 'Microsoft.App/containerApps@2024-10-02-preview' = { service: (includeAddOns && !empty(service)) ? service : null maxInactiveRevisions: maxInactiveRevisions registries: !empty(registries) ? registries : null - secrets: secretList + secrets: secrets runtime: { - dotnet: !empty(runtime.?dotnet) ? { - autoConfigureDataProtection: runtime.?dotnet.autoConfigureDataProtection - } : null - java: !empty(runtime.?java) ? { - enableMetrics: runtime.?java.enableMetrics - javaAgent: { - enabled: runtime.?java.enableJavaAgent - logging: { - loggerSettings: runtime.?java.?loggerSettings + dotnet: !empty(runtime.?dotnet) + ? { + autoConfigureDataProtection: runtime.?dotnet.autoConfigureDataProtection } - } - } : null + : null + java: !empty(runtime.?java) + ? { + enableMetrics: runtime.?java.enableMetrics + javaAgent: { + enabled: runtime.?java.enableJavaAgent + logging: { + loggerSettings: runtime.?java.?loggerSettings + } + } + } + : null } } template: { @@ -544,3 +545,20 @@ type runtimeType = { }[]? }? }? + +@export() +@description('The type for a secret.') +type secretType = { + @description('Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity.') + identity: string? + + @description('Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null.') + keyVaultUrl: string? + + @description('Optional. The name of the secret.') + name: string? + + @description('Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null.') + @secure() + value: string? +} diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json index 2cf631732b..88cf79e58a 100644 --- a/avm/res/app/container-app/main.json +++ b/avm/res/app/container-app/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10576304396294722473" + "templateHash": "7075159430454310469" }, "name": "Container Apps", "description": "This module deploys a Container App." @@ -512,6 +512,43 @@ "description": "Optional. App runtime configuration for the Container App." } }, + "secretType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity." + } + }, + "keyVaultUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the secret." + } + }, + "value": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a secret." + } + }, "lockType": { "type": "object", "properties": { @@ -934,8 +971,11 @@ } }, "secrets": { - "type": "secureObject", - "defaultValue": {}, + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, "metadata": { "description": "Optional. The secrets of the Container App." } @@ -970,7 +1010,6 @@ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], - "secretList": "[if(not(empty(parameters('secrets'))), parameters('secrets').secureList, createArray())]", "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), 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'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -1019,7 +1058,7 @@ "service": "[if(and(parameters('includeAddOns'), not(empty(parameters('service')))), parameters('service'), null())]", "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]", "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]", - "secrets": "[variables('secretList')]", + "secrets": "[parameters('secrets')]", "runtime": { "dotnet": "[if(not(empty(tryGet(parameters('runtime'), 'dotnet'))), createObject('autoConfigureDataProtection', tryGet(parameters('runtime'), 'dotnet', 'autoConfigureDataProtection')), null())]", "java": "[if(not(empty(tryGet(parameters('runtime'), 'java'))), createObject('enableMetrics', tryGet(parameters('runtime'), 'java', 'enableMetrics'), 'javaAgent', createObject('enabled', tryGet(parameters('runtime'), 'java', 'enableJavaAgent'), 'logging', createObject('loggerSettings', tryGet(tryGet(parameters('runtime'), 'java'), 'loggerSettings')))), null())]" diff --git a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep index c534e47cb3..bfc7880b4a 100644 --- a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation containers: [ { name: 'simple-hello-world-container' @@ -65,8 +64,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep index ffe04696b9..694d877bab 100644 --- a/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation disableIngress: true containers: [ { diff --git a/avm/res/app/container-app/tests/e2e/max/main.test.bicep b/avm/res/app/container-app/tests/e2e/max/main.test.bicep index b2b2d87fd4..67a05f663f 100644 --- a/avm/res/app/container-app/tests/e2e/max/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/max/main.test.bicep @@ -95,19 +95,17 @@ module testDeployment '../../../main.bicep' = [ nestedDependencies.outputs.managedIdentityResourceId ] } - secrets: { - secureList: [ - { - name: 'containerappstoredsecret' - value: myCustomContainerAppSecret - } - { - name: 'keyvaultstoredsecret' - keyVaultUrl: nestedDependencies.outputs.keyVaultSecretURI - identity: nestedDependencies.outputs.managedIdentityResourceId - } - ] - } + secrets: [ + { + name: 'containerappstoredsecret' + value: myCustomContainerAppSecret + } + { + name: 'keyvaultstoredsecret' + keyVaultUrl: nestedDependencies.outputs.keyVaultSecretURI + identity: nestedDependencies.outputs.managedIdentityResourceId + } + ] containers: [ { name: 'simple-hello-world-container' @@ -150,7 +148,7 @@ module testDeployment '../../../main.bicep' = [ java: { enableJavaAgent: true enableMetrics: false - loggerSettings:[ + loggerSettings: [ { level: 'info' logger: 'test' diff --git a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep index 3c34893f8c..dd909e518e 100644 --- a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation ingressExternal: false ingressTransport: 'tcp' ingressAllowInsecure: false diff --git a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep index a25b1ec60d..0bbe6788df 100644 --- a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep @@ -59,11 +59,6 @@ module testDeployment '../../../main.bicep' = [ Env: 'test' } environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } managedIdentities: { userAssignedResourceIds: [ nestedDependencies.outputs.managedIdentityResourceId @@ -98,8 +93,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json index 15548e8d1a..7466cbe674 100644 --- a/avm/res/app/container-app/version.json +++ b/avm/res/app/container-app/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ] From 0d5e4da2596817be1d39dedef48046aac4505ca2 Mon Sep 17 00:00:00 2001 From: DanteMustCode <52395211+DanteMustCode@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:33:58 +0800 Subject: [PATCH 23/53] feat: New module `avm/res/azure-stack-hci/cluster` module (#4417) ## Description Adds new Azure Stack HCI Cluster AVM module. Some helper modules for e2e testing are placed here `avm\utilities\e2e-template-assets\templates\azure-stack-hci`, which will also be using them for other HCI resource modules (always needing to deploy an HCI cluster in Azure to test on). If this is not the right location for this sort of shared asset, please let us know. For some reason Set-AVMModule does not generate cross-ref correctly on my dev machine, it is manually added back to pass the static validation. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.azure-stack-hci.cluster](https://github.com/Infrastructure-as-code-Automation/bicep-registry-modules/actions/workflows/avm.res.azure-stack-hci.cluster.yml/badge.svg?branch=user%2FDanteMustCode%2Fhci-cs)](https://github.com/Infrastructure-as-code-Automation/bicep-registry-modules/actions/workflows/avm.res.azure-stack-hci.cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 As agreed offline, [the existing draft pull request](https://github.com/Azure/bicep-registry-modules/pull/4155) by @AlexanderSehr and [the previous pull request](https://github.com/Azure/bicep-registry-modules/pull/3364) by @mbrat2005 will be superseded by this. Special thanks to their contribution to this pull request. ## Maintainer checklist: * [ ] Microsoft.AzureStackHCI RP registered (in order to grant permissions before starting) * [ ] Service Principal created and granted Azure Resource Bridge Deployment role on the management group (deployment will create subscription-level permissions, but parallel deployments/failures may clean up the required role). Create a secret credential. * [ ] CI-arbDeploymentAppId * [ ] CI-arbDeploymentSPObjectId (from Enterprise Application) * [ ] CI-arbDeploymentServicePrincipalSecret * [ ] CI-hciResourceProviderObjectId (Get-AzADServicePrincipal -ApplicationId 1412d89f-b8a8-4111-b4fd-e82905cbd85d) --------- Co-authored-by: Matthew Bratschun Co-authored-by: Matthew Bratschun <25390936+mbrat2005@users.noreply.github.com> Co-authored-by: Alexander Sehr Co-authored-by: Hangyu Xu --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.res.azure-stack-hci.cluster.yml | 88 ++ avm/res/azure-stack-hci/cluster/README.md | 1327 +++++++++++++++++ .../cluster/deployment-setting/README.md | 349 +++++ .../cluster/deployment-setting/main.bicep | 246 +++ .../cluster/deployment-setting/main.json | 397 +++++ avm/res/azure-stack-hci/cluster/main.bicep | 405 +++++ avm/res/azure-stack-hci/cluster/main.json | 1273 ++++++++++++++++ .../ashciARBSPRoleAssignment.bicep | 19 + .../ashciPrereqs.bicep | 346 +++++ .../azureStackHCIHost/README.md | 67 + .../azureStackHCIHost/hciHostDeployment.bicep | 535 +++++++ .../modules/subscriptionRoleAssignment.bicep | 16 + .../scripts/hciHostStage1.ps1 | 53 + .../scripts/hciHostStage2.ps1 | 40 + .../scripts/hciHostStage3.ps1 | 333 +++++ .../scripts/hciHostStage4.ps1 | 41 + .../scripts/hciHostStage5.ps1 | 398 +++++ .../scripts/hciHostStage6.ps1 | 354 +++++ .../scripts/hciHostStage7.ps1 | 207 +++ .../azureStackHCIHost/scripts/proxyConfig.sh | 153 ++ .../scripts/proxyConfigArcGW.sh | 171 +++ .../tests/e2e/defaults/dependencies.bicep | 145 ++ .../tests/e2e/defaults/main.test.bicep | 176 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 145 ++ .../tests/e2e/waf-aligned/main.test.bicep | 187 +++ avm/res/azure-stack-hci/cluster/version.json | 7 + 28 files changed, 7480 insertions(+) create mode 100644 .github/workflows/avm.res.azure-stack-hci.cluster.yml create mode 100644 avm/res/azure-stack-hci/cluster/README.md create mode 100644 avm/res/azure-stack-hci/cluster/deployment-setting/README.md create mode 100644 avm/res/azure-stack-hci/cluster/deployment-setting/main.bicep create mode 100644 avm/res/azure-stack-hci/cluster/deployment-setting/main.json create mode 100644 avm/res/azure-stack-hci/cluster/main.bicep create mode 100644 avm/res/azure-stack-hci/cluster/main.json create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciARBSPRoleAssignment.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/README.md create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/modules/subscriptionRoleAssignment.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage1.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage2.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage3.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage4.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage5.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage6.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage7.ps1 create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfig.sh create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfigArcGW.sh create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/azure-stack-hci/cluster/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2a1ac4c544..6f48d83936 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -45,6 +45,7 @@ /avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/automation/automation-account/ @Azure/avm-res-automation-automationaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/azure-stack-hci/cluster/ @Azure/avm-res-azurestackhci-cluster-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/batch/batch-account/ @Azure/avm-res-batch-batchaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/cache/redis/ @Azure/avm-res-cache-redis-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/cdn/profile/ @Azure/avm-res-cdn-profile-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 8518406122..b79261069b 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -80,6 +80,7 @@ body: - "avm/res/app/job" - "avm/res/app/managed-environment" - "avm/res/automation/automation-account" + - "avm/res/azure-stack-hci/cluster" - "avm/res/batch/batch-account" - "avm/res/cache/redis" - "avm/res/cdn/profile" diff --git a/.github/workflows/avm.res.azure-stack-hci.cluster.yml b/.github/workflows/avm.res.azure-stack-hci.cluster.yml new file mode 100644 index 0000000000..f9a35e7914 --- /dev/null +++ b/.github/workflows/avm.res.azure-stack-hci.cluster.yml @@ -0,0 +1,88 @@ +name: "avm.res.azure-stack-hci.cluster" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.azure-stack-hci.cluster.yml" + - "avm/res/azure-stack-hci/cluster/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/azure-stack-hci/cluster" + workflowPath: ".github/workflows/avm.res.azure-stack-hci.cluster.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/azure-stack-hci/cluster/README.md b/avm/res/azure-stack-hci/cluster/README.md new file mode 100644 index 0000000000..4cb635ad3f --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/README.md @@ -0,0 +1,1327 @@ +# Azure Stack HCI Cluster `[Microsoft.AzureStackHCI/clusters]` + +This module deploys an Azure Stack HCI Cluster on the provided Arc Machines. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## 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.AzureStackHCI/clusters` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.AzureStackHCI/clusters) | +| `Microsoft.AzureStackHCI/clusters/deploymentSettings` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.AzureStackHCI/clusters/deploymentSettings) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/azure-stack-hci/cluster:`. + +- [Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration](#example-1-deploy-azure-stack-hci-cluster-in-azure-with-a-2-node-switched-configuration) +- [Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration WAF aligned](#example-2-deploy-azure-stack-hci-cluster-in-azure-with-a-2-node-switched-configuration-waf-aligned) + +### Example 1: _Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration_ + +This test deploys an Azure VM to host a 2 node switched Azure Stack HCI cluster, validates the cluster configuration, and then deploys the cluster. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/azure-stack-hci/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + deploymentSettings: { + clusterNodeNames: '' + clusterWitnessStorageAccountName: '' + customLocationName: 'ashc2nmin-location' + defaultGateway: '172.20.0.1' + deploymentPrefix: '' + dnsServers: [ + '172.20.0.1' + ] + domainFqdn: 'hci.local' + domainOUPath: '' + enableStorageAutoIp: true + endingIPAddress: '172.20.0.7' + keyVaultName: '' + networkIntents: [ + { + adapter: [ + 'mgmt' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'management' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Management' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'comp0' + 'comp1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'compute' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Compute' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'smb0' + 'smb1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'storage' + overrideAdapterProperty: true + overrideQosPolicy: true + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Storage' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + ] + startingIPAddress: '172.20.0.2' + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' + } + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "deploymentSettings": { + "value": { + "clusterNodeNames": "", + "clusterWitnessStorageAccountName": "", + "customLocationName": "ashc2nmin-location", + "defaultGateway": "172.20.0.1", + "deploymentPrefix": "", + "dnsServers": [ + "172.20.0.1" + ], + "domainFqdn": "hci.local", + "domainOUPath": "", + "enableStorageAutoIp": true, + "endingIPAddress": "172.20.0.7", + "keyVaultName": "", + "networkIntents": [ + { + "adapter": [ + "mgmt" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "management", + "overrideAdapterProperty": true, + "overrideQosPolicy": false, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Management" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + }, + { + "adapter": [ + "comp0", + "comp1" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "compute", + "overrideAdapterProperty": true, + "overrideQosPolicy": false, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Compute" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + }, + { + "adapter": [ + "smb0", + "smb1" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "storage", + "overrideAdapterProperty": true, + "overrideQosPolicy": true, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Storage" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + } + ], + "startingIPAddress": "172.20.0.2", + "storageConnectivitySwitchless": false, + "storageNetworks": [ + { + "adapterName": "smb0", + "vlan": "711" + }, + { + "adapterName": "smb1", + "vlan": "712" + } + ], + "subnetMask": "255.255.255.0" + } + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/azure-stack-hci/cluster:' + +// Required parameters +param name = '' +// Non-required parameters +param deploymentSettings = { + clusterNodeNames: '' + clusterWitnessStorageAccountName: '' + customLocationName: 'ashc2nmin-location' + defaultGateway: '172.20.0.1' + deploymentPrefix: '' + dnsServers: [ + '172.20.0.1' + ] + domainFqdn: 'hci.local' + domainOUPath: '' + enableStorageAutoIp: true + endingIPAddress: '172.20.0.7' + keyVaultName: '' + networkIntents: [ + { + adapter: [ + 'mgmt' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'management' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Management' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'comp0' + 'comp1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'compute' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Compute' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'smb0' + 'smb1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'storage' + overrideAdapterProperty: true + overrideQosPolicy: true + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Storage' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + ] + startingIPAddress: '172.20.0.2' + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' +} +``` + +
+

+ +### Example 2: _Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration WAF aligned_ + +This test deploys an Azure VM to host a 2 node switched Azure Stack HCI cluster, validates the cluster configuration, and then deploys the cluster WAF aligned. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/azure-stack-hci/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + deploymentSettings: { + bitlockerBootVolume: true + bitlockerDataVolumes: true + clusterNodeNames: '' + clusterWitnessStorageAccountName: '' + customLocationName: 'ashc2nwaf-location' + defaultGateway: '172.20.0.1' + deploymentPrefix: '' + dnsServers: [ + '172.20.0.1' + ] + domainFqdn: 'hci.local' + domainOUPath: '' + driftControlEnforced: true + enableStorageAutoIp: true + endingIPAddress: '172.20.0.7' + keyVaultName: '' + networkIntents: [ + { + adapter: [ + 'mgmt' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'management' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Management' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'comp0' + 'comp1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'compute' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Compute' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'smb0' + 'smb1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'storage' + overrideAdapterProperty: true + overrideQosPolicy: true + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Storage' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + ] + sideChannelMitigationEnforced: true + smbClusterEncryption: true + smbSigningEnforced: true + startingIPAddress: '172.20.0.2' + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' + } + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "deploymentSettings": { + "value": { + "bitlockerBootVolume": true, + "bitlockerDataVolumes": true, + "clusterNodeNames": "", + "clusterWitnessStorageAccountName": "", + "customLocationName": "ashc2nwaf-location", + "defaultGateway": "172.20.0.1", + "deploymentPrefix": "", + "dnsServers": [ + "172.20.0.1" + ], + "domainFqdn": "hci.local", + "domainOUPath": "", + "driftControlEnforced": true, + "enableStorageAutoIp": true, + "endingIPAddress": "172.20.0.7", + "keyVaultName": "", + "networkIntents": [ + { + "adapter": [ + "mgmt" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "management", + "overrideAdapterProperty": true, + "overrideQosPolicy": false, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Management" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + }, + { + "adapter": [ + "comp0", + "comp1" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "compute", + "overrideAdapterProperty": true, + "overrideQosPolicy": false, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Compute" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + }, + { + "adapter": [ + "smb0", + "smb1" + ], + "adapterPropertyOverrides": { + "jumboPacket": "9014", + "networkDirect": "Disabled", + "networkDirectTechnology": "iWARP" + }, + "name": "storage", + "overrideAdapterProperty": true, + "overrideQosPolicy": true, + "overrideVirtualSwitchConfiguration": false, + "qosPolicyOverrides": { + "bandwidthPercentage_SMB": "50", + "priorityValue8021Action_Cluster": "7", + "priorityValue8021Action_SMB": "3" + }, + "trafficType": [ + "Storage" + ], + "virtualSwitchConfigurationOverrides": { + "enableIov": "true", + "loadBalancingAlgorithm": "Dynamic" + } + } + ], + "sideChannelMitigationEnforced": true, + "smbClusterEncryption": true, + "smbSigningEnforced": true, + "startingIPAddress": "172.20.0.2", + "storageConnectivitySwitchless": false, + "storageNetworks": [ + { + "adapterName": "smb0", + "vlan": "711" + }, + { + "adapterName": "smb1", + "vlan": "712" + } + ], + "subnetMask": "255.255.255.0" + } + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/azure-stack-hci/cluster:' + +// Required parameters +param name = '' +// Non-required parameters +param deploymentSettings = { + bitlockerBootVolume: true + bitlockerDataVolumes: true + clusterNodeNames: '' + clusterWitnessStorageAccountName: '' + customLocationName: 'ashc2nwaf-location' + defaultGateway: '172.20.0.1' + deploymentPrefix: '' + dnsServers: [ + '172.20.0.1' + ] + domainFqdn: 'hci.local' + domainOUPath: '' + driftControlEnforced: true + enableStorageAutoIp: true + endingIPAddress: '172.20.0.7' + keyVaultName: '' + networkIntents: [ + { + adapter: [ + 'mgmt' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'management' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Management' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'comp0' + 'comp1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'compute' + overrideAdapterProperty: true + overrideQosPolicy: false + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Compute' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + { + adapter: [ + 'smb0' + 'smb1' + ] + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + name: 'storage' + overrideAdapterProperty: true + overrideQosPolicy: true + overrideVirtualSwitchConfiguration: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + trafficType: [ + 'Storage' + ] + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + } + ] + sideChannelMitigationEnforced: true + smbClusterEncryption: true + smbSigningEnforced: true + startingIPAddress: '172.20.0.2' + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' +} +param tags = { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`deploymentOperations`](#parameter-deploymentoperations) | array | The cluster deployment operations to execute. Defaults to "[Validate, Deploy]". | +| [`deploymentSettings`](#parameter-deploymentsettings) | object | The deployment settings of the cluster. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`useSharedKeyVault`](#parameter-usesharedkeyvault) | bool | Specify whether to use the shared key vault for the HCI cluster. | + +### Parameter: `name` + +The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. + +- Required: Yes +- Type: string + +### Parameter: `deploymentOperations` + +The cluster deployment operations to execute. Defaults to "[Validate, Deploy]". + +- Required: No +- Type: array +- Default: + ```Bicep + [ + 'Deploy' + 'Validate' + ] + ``` +- Allowed: + ```Bicep + [ + 'Deploy' + 'Validate' + ] + ``` + +### Parameter: `deploymentSettings` + +The deployment settings of the cluster. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`clusterNodeNames`](#parameter-deploymentsettingsclusternodenames) | array | Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]. | +| [`clusterWitnessStorageAccountName`](#parameter-deploymentsettingsclusterwitnessstorageaccountname) | string | The name of the storage account to be used as the witness for the HCI Windows Failover Cluster. | +| [`customLocationName`](#parameter-deploymentsettingscustomlocationname) | string | The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01. | +| [`defaultGateway`](#parameter-deploymentsettingsdefaultgateway) | string | The default gateway of the Management Network. Example: 192.168.0.1. | +| [`deploymentPrefix`](#parameter-deploymentsettingsdeploymentprefix) | string | The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$. | +| [`dnsServers`](#parameter-deploymentsettingsdnsservers) | array | The DNS servers accessible from the Management Network for the HCI cluster. | +| [`domainFqdn`](#parameter-deploymentsettingsdomainfqdn) | string | The domain name of the Active Directory Domain Services. Example: "contoso.com". | +| [`domainOUPath`](#parameter-deploymentsettingsdomainoupath) | string | The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com". | +| [`endingIPAddress`](#parameter-deploymentsettingsendingipaddress) | string | The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. | +| [`keyVaultName`](#parameter-deploymentsettingskeyvaultname) | string | The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster. | +| [`networkIntents`](#parameter-deploymentsettingsnetworkintents) | array | An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster. | +| [`startingIPAddress`](#parameter-deploymentsettingsstartingipaddress) | string | The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. | +| [`storageConnectivitySwitchless`](#parameter-deploymentsettingsstorageconnectivityswitchless) | bool | Specify whether the Storage Network connectivity is switched or switchless. | +| [`storageNetworks`](#parameter-deploymentsettingsstoragenetworks) | array | An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations. | +| [`subnetMask`](#parameter-deploymentsettingssubnetmask) | string | The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`bitlockerBootVolume`](#parameter-deploymentsettingsbitlockerbootvolume) | bool | When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. | +| [`bitlockerDataVolumes`](#parameter-deploymentsettingsbitlockerdatavolumes) | bool | When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. | +| [`cloudId`](#parameter-deploymentsettingscloudid) | string | If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource. | +| [`credentialGuardEnforced`](#parameter-deploymentsettingscredentialguardenforced) | bool | Enables the Credential Guard. | +| [`driftControlEnforced`](#parameter-deploymentsettingsdriftcontrolenforced) | bool | When set to true, the security baseline is re-applied regularly. | +| [`drtmProtection`](#parameter-deploymentsettingsdrtmprotection) | bool | The hardware-dependent Secure Boot setting. | +| [`enableStorageAutoIp`](#parameter-deploymentsettingsenablestorageautoip) | bool | Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false. | +| [`episodicDataUpload`](#parameter-deploymentsettingsepisodicdataupload) | bool | The diagnostic data for deploying a HCI cluster. | +| [`hvciProtection`](#parameter-deploymentsettingshvciprotection) | bool | The Hypervisor-protected Code Integrity setting. | +| [`isEuropeanUnionLocation`](#parameter-deploymentsettingsiseuropeanunionlocation) | bool | The location data for deploying a HCI cluster. | +| [`sideChannelMitigationEnforced`](#parameter-deploymentsettingssidechannelmitigationenforced) | bool | When set to true, all the side channel mitigations are enabled. | +| [`smbClusterEncryption`](#parameter-deploymentsettingssmbclusterencryption) | bool | When set to true, cluster east-west traffic is encrypted. | +| [`smbSigningEnforced`](#parameter-deploymentsettingssmbsigningenforced) | bool | When set to true, the SMB default instance requires sign in for the client and server services. | +| [`storageConfigurationMode`](#parameter-deploymentsettingsstorageconfigurationmode) | string | The storage volume configuration mode. See documentation for details. | +| [`streamingDataClient`](#parameter-deploymentsettingsstreamingdataclient) | bool | The metrics data for deploying a HCI cluster. | +| [`wdacEnforced`](#parameter-deploymentsettingswdacenforced) | bool | Limits the applications and the code that you can run on your Azure Stack HCI cluster. | + +### Parameter: `deploymentSettings.clusterNodeNames` + +Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]. + +- Required: Yes +- Type: array + +### Parameter: `deploymentSettings.clusterWitnessStorageAccountName` + +The name of the storage account to be used as the witness for the HCI Windows Failover Cluster. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.customLocationName` + +The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.defaultGateway` + +The default gateway of the Management Network. Example: 192.168.0.1. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.deploymentPrefix` + +The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.dnsServers` + +The DNS servers accessible from the Management Network for the HCI cluster. + +- Required: Yes +- Type: array + +### Parameter: `deploymentSettings.domainFqdn` + +The domain name of the Active Directory Domain Services. Example: "contoso.com". + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.domainOUPath` + +The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com". + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.endingIPAddress` + +The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.keyVaultName` + +The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.networkIntents` + +An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster. + +- Required: Yes +- Type: array + +### Parameter: `deploymentSettings.startingIPAddress` + +The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.storageConnectivitySwitchless` + +Specify whether the Storage Network connectivity is switched or switchless. + +- Required: Yes +- Type: bool + +### Parameter: `deploymentSettings.storageNetworks` + +An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations. + +- Required: Yes +- Type: array + +### Parameter: `deploymentSettings.subnetMask` + +The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0. + +- Required: Yes +- Type: string + +### Parameter: `deploymentSettings.bitlockerBootVolume` + +When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.bitlockerDataVolumes` + +When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.cloudId` + +If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource. + +- Required: No +- Type: string + +### Parameter: `deploymentSettings.credentialGuardEnforced` + +Enables the Credential Guard. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.driftControlEnforced` + +When set to true, the security baseline is re-applied regularly. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.drtmProtection` + +The hardware-dependent Secure Boot setting. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.enableStorageAutoIp` + +Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.episodicDataUpload` + +The diagnostic data for deploying a HCI cluster. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.hvciProtection` + +The Hypervisor-protected Code Integrity setting. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.isEuropeanUnionLocation` + +The location data for deploying a HCI cluster. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.sideChannelMitigationEnforced` + +When set to true, all the side channel mitigations are enabled. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.smbClusterEncryption` + +When set to true, cluster east-west traffic is encrypted. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.smbSigningEnforced` + +When set to true, the SMB default instance requires sign in for the client and server services. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.storageConfigurationMode` + +The storage volume configuration mode. See documentation for details. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Express' + 'InfraOnly' + 'KeepStorage' + ] + ``` + +### Parameter: `deploymentSettings.streamingDataClient` + +The metrics data for deploying a HCI cluster. + +- Required: No +- Type: bool + +### Parameter: `deploymentSettings.wdacEnforced` + +Limits the applications and the code that you can run on your Azure Stack HCI cluster. + +- Required: No +- Type: bool + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Azure Stack HCI Administrator'` + - `'Windows Admin Center Administrator Login'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `useSharedKeyVault` + +Specify whether to use the shared key vault for the HCI cluster. + +- Required: No +- Type: bool +- Default: `True` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location of the cluster. | +| `name` | string | The name of the cluster. | +| `resourceGroupName` | string | The resource group of the cluster. | +| `resourceId` | string | The ID of the cluster. | +| `systemAssignedMIPrincipalId` | string | The managed identity of the cluster. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/azure-stack-hci/cluster/deployment-setting/README.md b/avm/res/azure-stack-hci/cluster/deployment-setting/README.md new file mode 100644 index 0000000000..fa18c97ce3 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/deployment-setting/README.md @@ -0,0 +1,349 @@ +# Azure Stack HCI Cluster Deployment Settings `[Microsoft.AzureStackHCI/clusters/deploymentSettings]` + +This module deploys an Azure Stack HCI Cluster Deployment Settings resource. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AzureStackHCI/clusters/deploymentSettings` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.AzureStackHCI/clusters/deploymentSettings) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`clusterNodeNames`](#parameter-clusternodenames) | array | Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]. | +| [`clusterWitnessStorageAccountName`](#parameter-clusterwitnessstorageaccountname) | string | The name of the storage account to be used as the witness for the HCI Windows Failover Cluster. | +| [`customLocationName`](#parameter-customlocationname) | string | The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01. | +| [`defaultGateway`](#parameter-defaultgateway) | string | The default gateway of the Management Network. Example: 192.168.0.1. | +| [`deploymentMode`](#parameter-deploymentmode) | string | First must pass with this parameter set to Validate prior running with it set to Deploy. If either Validation or Deployment phases fail, fix the issue, then resubmit the template with the same deploymentMode to retry. | +| [`deploymentPrefix`](#parameter-deploymentprefix) | string | The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$. | +| [`dnsServers`](#parameter-dnsservers) | array | The DNS servers accessible from the Management Network for the HCI cluster. | +| [`domainFqdn`](#parameter-domainfqdn) | string | The domain name of the Active Directory Domain Services. Example: "contoso.com". | +| [`domainOUPath`](#parameter-domainoupath) | string | The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com". | +| [`endingIPAddress`](#parameter-endingipaddress) | string | The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. | +| [`keyVaultName`](#parameter-keyvaultname) | string | The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster. | +| [`networkIntents`](#parameter-networkintents) | array | An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster. | +| [`startingIPAddress`](#parameter-startingipaddress) | string | The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. | +| [`storageConnectivitySwitchless`](#parameter-storageconnectivityswitchless) | bool | Specify whether the Storage Network connectivity is switched or switchless. | +| [`storageNetworks`](#parameter-storagenetworks) | array | An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations. | +| [`subnetMask`](#parameter-subnetmask) | string | The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`clusterName`](#parameter-clustername) | string | The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`bitlockerBootVolume`](#parameter-bitlockerbootvolume) | bool | When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. | +| [`bitlockerDataVolumes`](#parameter-bitlockerdatavolumes) | bool | When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. | +| [`cloudId`](#parameter-cloudid) | string | If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource. | +| [`credentialGuardEnforced`](#parameter-credentialguardenforced) | bool | Enables the Credential Guard. | +| [`driftControlEnforced`](#parameter-driftcontrolenforced) | bool | When set to true, the security baseline is re-applied regularly. | +| [`drtmProtection`](#parameter-drtmprotection) | bool | The hardware-dependent Secure Boot setting. | +| [`enableStorageAutoIp`](#parameter-enablestorageautoip) | bool | Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false. | +| [`episodicDataUpload`](#parameter-episodicdataupload) | bool | The diagnostic data for deploying a HCI cluster. | +| [`hvciProtection`](#parameter-hvciprotection) | bool | The Hypervisor-protected Code Integrity setting. | +| [`isEuropeanUnionLocation`](#parameter-iseuropeanunionlocation) | bool | The location data for deploying a HCI cluster. | +| [`name`](#parameter-name) | string | The name of the deployment settings. | +| [`sideChannelMitigationEnforced`](#parameter-sidechannelmitigationenforced) | bool | When set to true, all the side channel mitigations are enabled. | +| [`smbClusterEncryption`](#parameter-smbclusterencryption) | bool | When set to true, cluster east-west traffic is encrypted. | +| [`smbSigningEnforced`](#parameter-smbsigningenforced) | bool | When set to true, the SMB default instance requires sign in for the client and server services. | +| [`storageConfigurationMode`](#parameter-storageconfigurationmode) | string | The storage volume configuration mode. See documentation for details. | +| [`streamingDataClient`](#parameter-streamingdataclient) | bool | The metrics data for deploying a HCI cluster. | +| [`wdacEnforced`](#parameter-wdacenforced) | bool | Limits the applications and the code that you can run on your Azure Stack HCI cluster. | + +### Parameter: `clusterNodeNames` + +Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]. + +- Required: Yes +- Type: array + +### Parameter: `clusterWitnessStorageAccountName` + +The name of the storage account to be used as the witness for the HCI Windows Failover Cluster. + +- Required: Yes +- Type: string + +### Parameter: `customLocationName` + +The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01. + +- Required: Yes +- Type: string + +### Parameter: `defaultGateway` + +The default gateway of the Management Network. Example: 192.168.0.1. + +- Required: Yes +- Type: string + +### Parameter: `deploymentMode` + +First must pass with this parameter set to Validate prior running with it set to Deploy. If either Validation or Deployment phases fail, fix the issue, then resubmit the template with the same deploymentMode to retry. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Deploy' + 'Validate' + ] + ``` + +### Parameter: `deploymentPrefix` + +The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$. + +- Required: Yes +- Type: string + +### Parameter: `dnsServers` + +The DNS servers accessible from the Management Network for the HCI cluster. + +- Required: Yes +- Type: array + +### Parameter: `domainFqdn` + +The domain name of the Active Directory Domain Services. Example: "contoso.com". + +- Required: Yes +- Type: string + +### Parameter: `domainOUPath` + +The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com". + +- Required: Yes +- Type: string + +### Parameter: `endingIPAddress` + +The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. + +- Required: Yes +- Type: string + +### Parameter: `keyVaultName` + +The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster. + +- Required: Yes +- Type: string + +### Parameter: `networkIntents` + +An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster. + +- Required: Yes +- Type: array + +### Parameter: `startingIPAddress` + +The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs. + +- Required: Yes +- Type: string + +### Parameter: `storageConnectivitySwitchless` + +Specify whether the Storage Network connectivity is switched or switchless. + +- Required: Yes +- Type: bool + +### Parameter: `storageNetworks` + +An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations. + +- Required: Yes +- Type: array + +### Parameter: `subnetMask` + +The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0. + +- Required: Yes +- Type: string + +### Parameter: `clusterName` + +The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `bitlockerBootVolume` + +When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `bitlockerDataVolumes` + +When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `cloudId` + +If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource. + +- Required: No +- Type: string + +### Parameter: `credentialGuardEnforced` + +Enables the Credential Guard. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `driftControlEnforced` + +When set to true, the security baseline is re-applied regularly. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `drtmProtection` + +The hardware-dependent Secure Boot setting. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableStorageAutoIp` + +Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `episodicDataUpload` + +The diagnostic data for deploying a HCI cluster. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `hvciProtection` + +The Hypervisor-protected Code Integrity setting. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `isEuropeanUnionLocation` + +The location data for deploying a HCI cluster. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `name` + +The name of the deployment settings. + +- Required: No +- Type: string +- Default: `'default'` +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `sideChannelMitigationEnforced` + +When set to true, all the side channel mitigations are enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `smbClusterEncryption` + +When set to true, cluster east-west traffic is encrypted. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `smbSigningEnforced` + +When set to true, the SMB default instance requires sign in for the client and server services. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `storageConfigurationMode` + +The storage volume configuration mode. See documentation for details. + +- Required: No +- Type: string +- Default: `'Express'` +- Allowed: + ```Bicep + [ + 'Express' + 'InfraOnly' + 'KeepStorage' + ] + ``` + +### Parameter: `streamingDataClient` + +The metrics data for deploying a HCI cluster. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `wdacEnforced` + +Limits the applications and the code that you can run on your Azure Stack HCI cluster. + +- Required: No +- Type: bool +- Default: `True` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the cluster deployment settings. | +| `resourceGroupName` | string | The resource group of the cluster deployment settings. | +| `resourceId` | string | The ID of the cluster deployment settings. | diff --git a/avm/res/azure-stack-hci/cluster/deployment-setting/main.bicep b/avm/res/azure-stack-hci/cluster/deployment-setting/main.bicep new file mode 100644 index 0000000000..d7fb08a885 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/deployment-setting/main.bicep @@ -0,0 +1,246 @@ +metadata name = 'Azure Stack HCI Cluster Deployment Settings' +metadata description = 'This module deploys an Azure Stack HCI Cluster Deployment Settings resource.' + +@description('Optional. The name of the deployment settings.') +@allowed([ + 'default' +]) +param name string = 'default' + +@description('Conditional. The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. Required if the template is used in a standalone deployment.') +@maxLength(15) +@minLength(4) +param clusterName string + +@description('Required. First must pass with this parameter set to Validate prior running with it set to Deploy. If either Validation or Deployment phases fail, fix the issue, then resubmit the template with the same deploymentMode to retry.') +@allowed([ + 'Validate' + 'Deploy' +]) +param deploymentMode string + +@minLength(4) +@maxLength(8) +@description('Required. The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$.') +param deploymentPrefix string + +@description('Required. Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2].') +param clusterNodeNames array + +@description('Required. The domain name of the Active Directory Domain Services. Example: "contoso.com".') +param domainFqdn string + +@description('Required. The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com".') +param domainOUPath string + +@description('Optional. The Hypervisor-protected Code Integrity setting.') +param hvciProtection bool = true + +@description('Optional. The hardware-dependent Secure Boot setting.') +param drtmProtection bool = true + +@description('Optional. When set to true, the security baseline is re-applied regularly.') +param driftControlEnforced bool = false + +@description('Optional. Enables the Credential Guard.') +param credentialGuardEnforced bool = false + +@description('Optional. When set to true, the SMB default instance requires sign in for the client and server services.') +param smbSigningEnforced bool = false + +@description('Optional. When set to true, cluster east-west traffic is encrypted.') +param smbClusterEncryption bool = false + +@description('Optional. When set to true, all the side channel mitigations are enabled.') +param sideChannelMitigationEnforced bool = false + +@description('Optional. When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent.') +param bitlockerBootVolume bool = false + +@description('Optional. When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes.') +param bitlockerDataVolumes bool = false + +@description('Optional. Limits the applications and the code that you can run on your Azure Stack HCI cluster.') +param wdacEnforced bool = true + +// cluster diagnostics and telemetry configuration +@description('Optional. The metrics data for deploying a HCI cluster.') +param streamingDataClient bool = true + +@description('Optional. The location data for deploying a HCI cluster.') +param isEuropeanUnionLocation bool = false + +@description('Optional. The diagnostic data for deploying a HCI cluster.') +param episodicDataUpload bool = true + +// storage configuration +@description('Optional. The storage volume configuration mode. See documentation for details.') +@allowed([ + 'Express' + 'InfraOnly' + 'KeepStorage' +]) +param storageConfigurationMode string = 'Express' + +// cluster network configuration details +@description('Required. The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0.') +param subnetMask string + +@description('Required. The default gateway of the Management Network. Example: 192.168.0.1.') +param defaultGateway string + +@description('Required. The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs.') +param startingIPAddress string + +@description('Required. The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs.') +param endingIPAddress string + +@description('Required. The DNS servers accessible from the Management Network for the HCI cluster.') +param dnsServers string[] + +@description('Required. An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster.') +param networkIntents array + +@description('Required. Specify whether the Storage Network connectivity is switched or switchless.') +param storageConnectivitySwitchless bool + +@description('Optional. Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false.') +param enableStorageAutoIp bool = true + +@description('Required. An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations.') +param storageNetworks array + +// other cluster configuration parameters +@description('Required. The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01.') +param customLocationName string + +@description('Required. The name of the storage account to be used as the witness for the HCI Windows Failover Cluster.') +param clusterWitnessStorageAccountName string + +@description('Required. The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster.') +param keyVaultName string + +@description('Optional. If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource.') +param cloudId string? + +var arcNodeResourceIds = [ + for (nodeName, index) in clusterNodeNames: resourceId('Microsoft.HybridCompute/machines', nodeName) +] + +resource cluster 'Microsoft.AzureStackHCI/clusters@2024-04-01' existing = { + name: clusterName +} + +resource deploymentSettings 'Microsoft.AzureStackHCI/clusters/deploymentSettings@2024-04-01' = { + name: name + parent: cluster + properties: { + arcNodeResourceIds: arcNodeResourceIds + deploymentMode: deploymentMode + deploymentConfiguration: { + version: '10.0.0.0' + scaleUnits: [ + { + deploymentData: { + securitySettings: { + hvciProtection: hvciProtection + drtmProtection: drtmProtection + driftControlEnforced: driftControlEnforced + credentialGuardEnforced: credentialGuardEnforced + smbSigningEnforced: smbSigningEnforced + smbClusterEncryption: smbClusterEncryption + sideChannelMitigationEnforced: sideChannelMitigationEnforced + bitlockerBootVolume: bitlockerBootVolume + bitlockerDataVolumes: bitlockerDataVolumes + wdacEnforced: wdacEnforced + } + observability: { + streamingDataClient: streamingDataClient + euLocation: isEuropeanUnionLocation + episodicDataUpload: episodicDataUpload + } + cluster: { + name: clusterName + witnessType: 'Cloud' + witnessPath: '' + cloudAccountName: clusterWitnessStorageAccountName + azureServiceEndpoint: environment().suffixes.storage + } + storage: { + configurationMode: storageConfigurationMode + } + namingPrefix: deploymentPrefix + domainFqdn: domainFqdn + infrastructureNetwork: [ + { + subnetMask: subnetMask + gateway: defaultGateway + ipPools: [ + { + startingAddress: startingIPAddress + endingAddress: endingIPAddress + } + ] + dnsServers: dnsServers + } + ] + physicalNodes: [ + for hciNode in arcNodeResourceIds: { + name: reference(hciNode, '2022-12-27', 'Full').properties.displayName + // Getting the IP from the first NIC of the node with a default gateway. Only the first management pNIC should have a gateway defined. + // This reference call requires that the 'DeviceManagementExtension' extension be fully initialized on each node, which creates the + // edgeDevices sub-resource queried below, containing the IP configuration. View the edgedevice to troubleshoot by appending + // `/providers/microsoft.azurestackhci/edgedevices/default` to the end the HCI Arc Machine resource id and using `az resource show --id ` + ipv4Address: (filter( + reference('${hciNode}/providers/microsoft.azurestackhci/edgeDevices/default', '2024-01-01', 'Full').properties.deviceConfiguration.nicDetails, + nic => nic.?defaultGateway != null + ))[0].ip4Address + } + ] + hostNetwork: { + intents: networkIntents + storageConnectivitySwitchless: storageConnectivitySwitchless + storageNetworks: [ + for (storageAdapter, index) in storageNetworks: { + name: 'StorageNetwork${index + 1}' + networkAdapterName: storageAdapter.adapterName + vlanId: storageAdapter.vlan + storageAdapterIPInfo: storageAdapter.?storageAdapterIPInfo + } + ] + enableStorageAutoIp: enableStorageAutoIp + } + adouPath: domainOUPath + secretsLocation: 'https://${keyVaultName}${environment().suffixes.keyvaultDns}' + optionalServices: { + customLocation: customLocationName + } + secrets: [ + for secretName in [ + 'LocalAdminCredential' + 'AzureStackLCMUserCredential' + 'DefaultARBApplication' + 'WitnessStorageKey' + ]: { + secretName: empty(cloudId) ? secretName : '${clusterName}-${secretName}-${cloudId}' + eceSecretName: secretName + secretLocation: empty(cloudId) + ? 'https://${keyVaultName}${environment().suffixes.keyvaultDns}/secrets/${secretName}' + : 'https://${keyVaultName}${environment().suffixes.keyvaultDns}/secrets/${clusterName}-${secretName}-${cloudId}' + } + ] + } + } + ] + } + } +} + +@description('The name of the cluster deployment settings.') +output name string = deploymentSettings.name + +@description('The ID of the cluster deployment settings.') +output resourceId string = deploymentSettings.id + +@description('The resource group of the cluster deployment settings.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/azure-stack-hci/cluster/deployment-setting/main.json b/avm/res/azure-stack-hci/cluster/deployment-setting/main.json new file mode 100644 index 0000000000..2edb982bfa --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/deployment-setting/main.json @@ -0,0 +1,397 @@ +{ + "$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.33.93.31351", + "templateHash": "7982158538530558379" + }, + "name": "Azure Stack HCI Cluster Deployment Settings", + "description": "This module deploys an Azure Stack HCI Cluster Deployment Settings resource." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. The name of the deployment settings." + } + }, + "clusterName": { + "type": "string", + "minLength": 4, + "maxLength": 15, + "metadata": { + "description": "Conditional. The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. Required if the template is used in a standalone deployment." + } + }, + "deploymentMode": { + "type": "string", + "allowedValues": [ + "Validate", + "Deploy" + ], + "metadata": { + "description": "Required. First must pass with this parameter set to Validate prior running with it set to Deploy. If either Validation or Deployment phases fail, fix the issue, then resubmit the template with the same deploymentMode to retry." + } + }, + "deploymentPrefix": { + "type": "string", + "minLength": 4, + "maxLength": 8, + "metadata": { + "description": "Required. The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$." + } + }, + "clusterNodeNames": { + "type": "array", + "metadata": { + "description": "Required. Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]." + } + }, + "domainFqdn": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the Active Directory Domain Services. Example: \"contoso.com\"." + } + }, + "domainOUPath": { + "type": "string", + "metadata": { + "description": "Required. The ADDS OU path - ex \"OU=HCI,DC=contoso,DC=com\"." + } + }, + "hvciProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The Hypervisor-protected Code Integrity setting." + } + }, + "drtmProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The hardware-dependent Secure Boot setting." + } + }, + "driftControlEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, the security baseline is re-applied regularly." + } + }, + "credentialGuardEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables the Credential Guard." + } + }, + "smbSigningEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, the SMB default instance requires sign in for the client and server services." + } + }, + "smbClusterEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, cluster east-west traffic is encrypted." + } + }, + "sideChannelMitigationEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, all the side channel mitigations are enabled." + } + }, + "bitlockerBootVolume": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent." + } + }, + "bitlockerDataVolumes": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes." + } + }, + "wdacEnforced": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Limits the applications and the code that you can run on your Azure Stack HCI cluster." + } + }, + "streamingDataClient": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The metrics data for deploying a HCI cluster." + } + }, + "isEuropeanUnionLocation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The location data for deploying a HCI cluster." + } + }, + "episodicDataUpload": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The diagnostic data for deploying a HCI cluster." + } + }, + "storageConfigurationMode": { + "type": "string", + "defaultValue": "Express", + "allowedValues": [ + "Express", + "InfraOnly", + "KeepStorage" + ], + "metadata": { + "description": "Optional. The storage volume configuration mode. See documentation for details." + } + }, + "subnetMask": { + "type": "string", + "metadata": { + "description": "Required. The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0." + } + }, + "defaultGateway": { + "type": "string", + "metadata": { + "description": "Required. The default gateway of the Management Network. Example: 192.168.0.1." + } + }, + "startingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "endingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The DNS servers accessible from the Management Network for the HCI cluster." + } + }, + "networkIntents": { + "type": "array", + "metadata": { + "description": "Required. An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster." + } + }, + "storageConnectivitySwitchless": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether the Storage Network connectivity is switched or switchless." + } + }, + "enableStorageAutoIp": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false." + } + }, + "storageNetworks": { + "type": "array", + "metadata": { + "description": "Required. An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations." + } + }, + "customLocationName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01." + } + }, + "clusterWitnessStorageAccountName": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage account to be used as the witness for the HCI Windows Failover Cluster." + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster." + } + }, + "cloudId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource." + } + } + }, + "variables": { + "copy": [ + { + "name": "arcNodeResourceIds", + "count": "[length(parameters('clusterNodeNames'))]", + "input": "[resourceId('Microsoft.HybridCompute/machines', parameters('clusterNodeNames')[copyIndex('arcNodeResourceIds')])]" + } + ] + }, + "resources": { + "cluster": { + "existing": true, + "type": "Microsoft.AzureStackHCI/clusters", + "apiVersion": "2024-04-01", + "name": "[parameters('clusterName')]" + }, + "deploymentSettings": { + "type": "Microsoft.AzureStackHCI/clusters/deploymentSettings", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": { + "arcNodeResourceIds": "[variables('arcNodeResourceIds')]", + "deploymentMode": "[parameters('deploymentMode')]", + "deploymentConfiguration": { + "version": "10.0.0.0", + "scaleUnits": [ + { + "deploymentData": { + "copy": [ + { + "name": "physicalNodes", + "count": "[length(variables('arcNodeResourceIds'))]", + "input": { + "name": "[reference(variables('arcNodeResourceIds')[copyIndex('physicalNodes')], '2022-12-27', 'Full').properties.displayName]", + "ipv4Address": "[filter(reference(format('{0}/providers/microsoft.azurestackhci/edgeDevices/default', variables('arcNodeResourceIds')[copyIndex('physicalNodes')]), '2024-01-01', 'Full').properties.deviceConfiguration.nicDetails, lambda('nic', not(equals(tryGet(lambdaVariables('nic'), 'defaultGateway'), null()))))[0].ip4Address]" + } + }, + { + "name": "secrets", + "count": "[length(createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey'))]", + "input": { + "secretName": "[if(empty(parameters('cloudId')), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], format('{0}-{1}-{2}', parameters('clusterName'), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], parameters('cloudId')))]", + "eceSecretName": "[createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')]]", + "secretLocation": "[if(empty(parameters('cloudId')), format('https://{0}{1}/secrets/{2}', parameters('keyVaultName'), environment().suffixes.keyvaultDns, createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')]), format('https://{0}{1}/secrets/{2}-{3}-{4}', parameters('keyVaultName'), environment().suffixes.keyvaultDns, parameters('clusterName'), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], parameters('cloudId')))]" + } + } + ], + "securitySettings": { + "hvciProtection": "[parameters('hvciProtection')]", + "drtmProtection": "[parameters('drtmProtection')]", + "driftControlEnforced": "[parameters('driftControlEnforced')]", + "credentialGuardEnforced": "[parameters('credentialGuardEnforced')]", + "smbSigningEnforced": "[parameters('smbSigningEnforced')]", + "smbClusterEncryption": "[parameters('smbClusterEncryption')]", + "sideChannelMitigationEnforced": "[parameters('sideChannelMitigationEnforced')]", + "bitlockerBootVolume": "[parameters('bitlockerBootVolume')]", + "bitlockerDataVolumes": "[parameters('bitlockerDataVolumes')]", + "wdacEnforced": "[parameters('wdacEnforced')]" + }, + "observability": { + "streamingDataClient": "[parameters('streamingDataClient')]", + "euLocation": "[parameters('isEuropeanUnionLocation')]", + "episodicDataUpload": "[parameters('episodicDataUpload')]" + }, + "cluster": { + "name": "[parameters('clusterName')]", + "witnessType": "Cloud", + "witnessPath": "", + "cloudAccountName": "[parameters('clusterWitnessStorageAccountName')]", + "azureServiceEndpoint": "[environment().suffixes.storage]" + }, + "storage": { + "configurationMode": "[parameters('storageConfigurationMode')]" + }, + "namingPrefix": "[parameters('deploymentPrefix')]", + "domainFqdn": "[parameters('domainFqdn')]", + "infrastructureNetwork": [ + { + "subnetMask": "[parameters('subnetMask')]", + "gateway": "[parameters('defaultGateway')]", + "ipPools": [ + { + "startingAddress": "[parameters('startingIPAddress')]", + "endingAddress": "[parameters('endingIPAddress')]" + } + ], + "dnsServers": "[parameters('dnsServers')]" + } + ], + "hostNetwork": { + "copy": [ + { + "name": "storageNetworks", + "count": "[length(parameters('storageNetworks'))]", + "input": { + "name": "[format('StorageNetwork{0}', add(copyIndex('storageNetworks'), 1))]", + "networkAdapterName": "[parameters('storageNetworks')[copyIndex('storageNetworks')].adapterName]", + "vlanId": "[parameters('storageNetworks')[copyIndex('storageNetworks')].vlan]", + "storageAdapterIPInfo": "[tryGet(parameters('storageNetworks')[copyIndex('storageNetworks')], 'storageAdapterIPInfo')]" + } + } + ], + "intents": "[parameters('networkIntents')]", + "storageConnectivitySwitchless": "[parameters('storageConnectivitySwitchless')]", + "enableStorageAutoIp": "[parameters('enableStorageAutoIp')]" + }, + "adouPath": "[parameters('domainOUPath')]", + "secretsLocation": "[format('https://{0}{1}', parameters('keyVaultName'), environment().suffixes.keyvaultDns)]", + "optionalServices": { + "customLocation": "[parameters('customLocationName')]" + } + } + } + ] + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cluster deployment settings." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the cluster deployment settings." + }, + "value": "[resourceId('Microsoft.AzureStackHCI/clusters/deploymentSettings', parameters('clusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the cluster deployment settings." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/azure-stack-hci/cluster/main.bicep b/avm/res/azure-stack-hci/cluster/main.bicep new file mode 100644 index 0000000000..565a4150b2 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/main.bicep @@ -0,0 +1,405 @@ +metadata name = 'Azure Stack HCI Cluster' +metadata description = 'This module deploys an Azure Stack HCI Cluster on the provided Arc Machines.' + +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure.') +@maxLength(15) +@minLength(4) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. The cluster deployment operations to execute. Defaults to "[Validate, Deploy]".') +@allowed([ + 'Deploy' + 'Validate' +]) +param deploymentOperations string[] = ['Validate', 'Deploy'] + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The deployment settings of the cluster.') +param deploymentSettings deploymentSettingsType? + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +@description('Optional. Specify whether to use the shared key vault for the HCI cluster.') +param useSharedKeyVault bool = true + +// ============= // +// Variables // +// ============= // + +var builtInRoleNames = { + // Add other relevant built-in roles here for your resource as per BCPNFR5 + 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': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) + 'Azure Stack HCI Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'bda0d508-adf1-4af0-9c28-88919fc3ae06' + ) + 'Windows Admin Center Administrator Login': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'a6333a3e-0164-44c3-b281-7a577aff287f' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +// if deployment operations requested, validation must be performed first so we reverse sort the array +var sortedDeploymentOperations = (!empty(deploymentOperations)) ? sort(deploymentOperations, (a, b) => a > b) : [] + +// ============= // +// Resources // +// ============= // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: take( + '46d3xbcp.res.azurestackhci-cluster.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', + 64 + ) + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource cluster 'Microsoft.AzureStackHCI/clusters@2024-04-01' = { + name: name + identity: { + type: 'SystemAssigned' + } + location: location + properties: {} + tags: tags +} +@batchSize(1) +module deploymentSetting 'deployment-setting/main.bicep' = [ + for deploymentOperation in sortedDeploymentOperations: if (!empty(deploymentOperation) && !empty(deploymentSettings)) { + name: 'deploymentSettings-${deploymentOperation}' + params: { + cloudId: useSharedKeyVault ? cluster.properties.cloudId : null + clusterName: cluster.name + deploymentMode: deploymentOperation + clusterNodeNames: deploymentSettings!.clusterNodeNames + clusterWitnessStorageAccountName: deploymentSettings!.clusterWitnessStorageAccountName + customLocationName: deploymentSettings!.customLocationName + defaultGateway: deploymentSettings!.defaultGateway + deploymentPrefix: deploymentSettings!.deploymentPrefix + dnsServers: deploymentSettings!.dnsServers + domainFqdn: deploymentSettings!.domainFqdn + domainOUPath: deploymentSettings!.domainOUPath + endingIPAddress: deploymentSettings!.endingIPAddress + keyVaultName: deploymentSettings!.keyVaultName + networkIntents: deploymentSettings!.networkIntents + startingIPAddress: deploymentSettings!.startingIPAddress + storageConnectivitySwitchless: deploymentSettings!.storageConnectivitySwitchless + storageNetworks: deploymentSettings!.storageNetworks + subnetMask: deploymentSettings!.subnetMask + bitlockerBootVolume: deploymentSettings!.?bitlockerBootVolume + bitlockerDataVolumes: deploymentSettings!.?bitlockerDataVolumes + credentialGuardEnforced: deploymentSettings!.?credentialGuardEnforced + driftControlEnforced: deploymentSettings!.?driftControlEnforced + drtmProtection: deploymentSettings!.?drtmProtection + enableStorageAutoIp: deploymentSettings!.?enableStorageAutoIp + episodicDataUpload: deploymentSettings!.?episodicDataUpload + hvciProtection: deploymentSettings!.?hvciProtection + isEuropeanUnionLocation: deploymentSettings!.?isRFEuropeanUnionLocation + sideChannelMitigationEnforced: deploymentSettings!.?sideChannelMitigationEnforced + smbClusterEncryption: deploymentSettings!.?smbClusterEncryption + smbSigningEnforced: deploymentSettings!.?smbSigningEnforced + storageConfigurationMode: deploymentSettings!.?storageConfigurationMode + streamingDataClient: deploymentSettings!.?streamingDataClient + wdacEnforced: deploymentSettings!.?wdacEnforced + } + } +] + +resource cluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(cluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: cluster + } +] + +@description('The name of the cluster.') +output name string = cluster.name + +@description('The ID of the cluster.') +output resourceId string = cluster.id + +@description('The resource group of the cluster.') +output resourceGroupName string = resourceGroup().name + +@description('The managed identity of the cluster.') +output systemAssignedMIPrincipalId string = cluster.identity.principalId + +@description('The location of the cluster.') +output location string = cluster.location + +// =============== // +// Definitions // +// =============== // + +@export() +type networkIntentType = { + @description('Required. The names of the network adapters to include in the intent.') + adapter: string[] + + @description('Required. The name of the network intent.') + name: string + + @description('Required. Specify whether to override the adapter property. Use false by default.') + overrideAdapterProperty: bool + + @description('Required. The adapter property overrides for the network intent.') + adapterPropertyOverrides: { + @description('Required. The jumboPacket configuration for the network adapters.') + jumboPacket: string + + @description('Required. The networkDirect configuration for the network adapters.') + networkDirect: ('Enabled' | 'Disabled') + + @description('Required. The networkDirectTechnology configuration for the network adapters.') + networkDirectTechnology: ('RoCEv2' | 'iWARP') + } + + @description('Required. Specify whether to override the qosPolicy property. Use false by default.') + overrideQosPolicy: bool + + @description('Required. The qosPolicy overrides for the network intent.') + qosPolicyOverrides: { + @description('Required. The bandwidthPercentage for the network intent. Recommend 50.') + bandwidthPercentage_SMB: string + + @description('Required. Recommend 7.') + priorityValue8021Action_Cluster: string + + @description('Required. Recommend 3.') + priorityValue8021Action_SMB: string + } + + @description('Required. Specify whether to override the virtualSwitchConfiguration property. Use false by default.') + overrideVirtualSwitchConfiguration: bool + + @description('Required. The virtualSwitchConfiguration overrides for the network intent.') + virtualSwitchConfigurationOverrides: { + @description('Required. The enableIov configuration for the network intent.') + enableIov: ('true' | 'false') + + @description('Required. The loadBalancingAlgorithm configuration for the network intent.') + loadBalancingAlgorithm: ('Dynamic' | 'HyperVPort' | 'IPHash') + } + + @description('Required. The traffic types for the network intent.') + trafficType: ('Compute' | 'Management' | 'Storage')[] +} + +// define custom type for storage adapter IP info for 3-node switchless deployments +@export() +type storageAdapterIPInfoType = { + @description('Required. The HCI node name.') + physicalNode: string + + @description('Required. The IPv4 address for the storage adapter.') + ipv4Address: string + + @description('Required. The subnet mask for the storage adapter.') + subnetMask: string +} + +// define custom type for storage network objects +@export() +type storageNetworksType = { + @description('Required. The name of the storage adapter.') + adapterName: string + + @description('Required. The VLAN for the storage adapter.') + vlan: string + + @description('Optional. The storage adapter IP information for 3-node switchless or manual config deployments.') + storageAdapterIPInfo: storageAdapterIPInfoType[]? // optional for switched deployments +} + +// cluster security configuration settings +@export() +type securityConfigurationType = { + @description('Required. Enable/Disable HVCI protection.') + hvciProtection: bool + + @description('Required. Enable/Disable DRTM protection.') + drtmProtection: bool + + @description('Required. Enable/Disable Drift Control enforcement.') + driftControlEnforced: bool + + @description('Required. Enable/Disable Credential Guard enforcement.') + credentialGuardEnforced: bool + + @description('Required. Enable/Disable SMB signing enforcement.') + smbSigningEnforced: bool + + @description('Required. Enable/Disable SMB cluster encryption.') + smbClusterEncryption: bool + + @description('Required. Enable/Disable Side Channel Mitigation enforcement.') + sideChannelMitigationEnforced: bool + + @description('Required. Enable/Disable BitLocker protection for boot volume.') + bitlockerBootVolume: bool + + @description('Required. Enable/Disable BitLocker protection for data volumes.') + bitlockerDataVolumes: bool + + @description('Required. Enable/Disable WDAC enforcement.') + wdacEnforced: bool +} + +type deploymentSettingsType = { + @minLength(4) + @maxLength(8) + @description('Required. The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$.') + deploymentPrefix: string + + @description('Required. Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2].') + clusterNodeNames: array + + @description('Required. The domain name of the Active Directory Domain Services. Example: "contoso.com".') + domainFqdn: string + + @description('Required. The ADDS OU path - ex "OU=HCI,DC=contoso,DC=com".') + domainOUPath: string + + @description('Optional. The Hypervisor-protected Code Integrity setting.') + hvciProtection: bool? + + @description('Optional. The hardware-dependent Secure Boot setting.') + drtmProtection: bool? + + @description('Optional. When set to true, the security baseline is re-applied regularly.') + driftControlEnforced: bool? + + @description('Optional. Enables the Credential Guard.') + credentialGuardEnforced: bool? + + @description('Optional. When set to true, the SMB default instance requires sign in for the client and server services.') + smbSigningEnforced: bool? + + @description('Optional. When set to true, cluster east-west traffic is encrypted.') + smbClusterEncryption: bool? + + @description('Optional. When set to true, all the side channel mitigations are enabled.') + sideChannelMitigationEnforced: bool? + + @description('Optional. When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent.') + bitlockerBootVolume: bool? + + @description('Optional. When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes.') + bitlockerDataVolumes: bool? + + @description('Optional. Limits the applications and the code that you can run on your Azure Stack HCI cluster.') + wdacEnforced: bool? + + // cluster diagnostics and telemetry configuration + @description('Optional. The metrics data for deploying a HCI cluster.') + streamingDataClient: bool? + + @description('Optional. The location data for deploying a HCI cluster.') + isEuropeanUnionLocation: bool? + + @description('Optional. The diagnostic data for deploying a HCI cluster.') + episodicDataUpload: bool? + + // storage configuration + @description('Optional. The storage volume configuration mode. See documentation for details.') + storageConfigurationMode: ('Express' | 'InfraOnly' | 'KeepStorage')? + + // cluster network configuration details + @description('Required. The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0.') + subnetMask: string + + @description('Required. The default gateway of the Management Network. Example: 192.168.0.1.') + defaultGateway: string + + @description('Required. The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs.') + startingIPAddress: string + + @description('Required. The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs.') + endingIPAddress: string + + @description('Required. The DNS servers accessible from the Management Network for the HCI cluster.') + dnsServers: string[] + + @description('Required. An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster.') + networkIntents: array + + @description('Required. Specify whether the Storage Network connectivity is switched or switchless.') + storageConnectivitySwitchless: bool + + @description('Optional. Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false.') + enableStorageAutoIp: bool? + + @description('Required. An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations.') + storageNetworks: array + + // other cluster configuration parameters + @description('Required. The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01.') + customLocationName: string + + @description('Required. The name of the storage account to be used as the witness for the HCI Windows Failover Cluster.') + clusterWitnessStorageAccountName: string + + @description('Required. The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster.') + keyVaultName: string + + @description('Optional. If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource.') + cloudId: string? +} diff --git a/avm/res/azure-stack-hci/cluster/main.json b/avm/res/azure-stack-hci/cluster/main.json new file mode 100644 index 0000000000..f082101b8d --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/main.json @@ -0,0 +1,1273 @@ +{ + "$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.33.93.31351", + "templateHash": "2661952410917464461" + }, + "name": "Azure Stack HCI Cluster", + "description": "This module deploys an Azure Stack HCI Cluster on the provided Arc Machines." + }, + "definitions": { + "networkIntentType": { + "type": "object", + "properties": { + "adapter": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The names of the network adapters to include in the intent." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network intent." + } + }, + "overrideAdapterProperty": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether to override the adapter property. Use false by default." + } + }, + "adapterPropertyOverrides": { + "type": "object", + "properties": { + "jumboPacket": { + "type": "string", + "metadata": { + "description": "Required. The jumboPacket configuration for the network adapters." + } + }, + "networkDirect": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Required. The networkDirect configuration for the network adapters." + } + }, + "networkDirectTechnology": { + "type": "string", + "allowedValues": [ + "RoCEv2", + "iWARP" + ], + "metadata": { + "description": "Required. The networkDirectTechnology configuration for the network adapters." + } + } + }, + "metadata": { + "description": "Required. The adapter property overrides for the network intent." + } + }, + "overrideQosPolicy": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether to override the qosPolicy property. Use false by default." + } + }, + "qosPolicyOverrides": { + "type": "object", + "properties": { + "bandwidthPercentage_SMB": { + "type": "string", + "metadata": { + "description": "Required. The bandwidthPercentage for the network intent. Recommend 50." + } + }, + "priorityValue8021Action_Cluster": { + "type": "string", + "metadata": { + "description": "Required. Recommend 7." + } + }, + "priorityValue8021Action_SMB": { + "type": "string", + "metadata": { + "description": "Required. Recommend 3." + } + } + }, + "metadata": { + "description": "Required. The qosPolicy overrides for the network intent." + } + }, + "overrideVirtualSwitchConfiguration": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether to override the virtualSwitchConfiguration property. Use false by default." + } + }, + "virtualSwitchConfigurationOverrides": { + "type": "object", + "properties": { + "enableIov": { + "type": "string", + "allowedValues": [ + "false", + "true" + ], + "metadata": { + "description": "Required. The enableIov configuration for the network intent." + } + }, + "loadBalancingAlgorithm": { + "type": "string", + "allowedValues": [ + "Dynamic", + "HyperVPort", + "IPHash" + ], + "metadata": { + "description": "Required. The loadBalancingAlgorithm configuration for the network intent." + } + } + }, + "metadata": { + "description": "Required. The virtualSwitchConfiguration overrides for the network intent." + } + }, + "trafficType": { + "type": "array", + "allowedValues": [ + "Compute", + "Management", + "Storage" + ], + "metadata": { + "description": "Required. The traffic types for the network intent." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "storageAdapterIPInfoType": { + "type": "object", + "properties": { + "physicalNode": { + "type": "string", + "metadata": { + "description": "Required. The HCI node name." + } + }, + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address for the storage adapter." + } + }, + "subnetMask": { + "type": "string", + "metadata": { + "description": "Required. The subnet mask for the storage adapter." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "storageNetworksType": { + "type": "object", + "properties": { + "adapterName": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage adapter." + } + }, + "vlan": { + "type": "string", + "metadata": { + "description": "Required. The VLAN for the storage adapter." + } + }, + "storageAdapterIPInfo": { + "type": "array", + "items": { + "$ref": "#/definitions/storageAdapterIPInfoType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The storage adapter IP information for 3-node switchless or manual config deployments." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "securityConfigurationType": { + "type": "object", + "properties": { + "hvciProtection": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable HVCI protection." + } + }, + "drtmProtection": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable DRTM protection." + } + }, + "driftControlEnforced": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable Drift Control enforcement." + } + }, + "credentialGuardEnforced": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable Credential Guard enforcement." + } + }, + "smbSigningEnforced": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable SMB signing enforcement." + } + }, + "smbClusterEncryption": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable SMB cluster encryption." + } + }, + "sideChannelMitigationEnforced": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable Side Channel Mitigation enforcement." + } + }, + "bitlockerBootVolume": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable BitLocker protection for boot volume." + } + }, + "bitlockerDataVolumes": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable BitLocker protection for data volumes." + } + }, + "wdacEnforced": { + "type": "bool", + "metadata": { + "description": "Required. Enable/Disable WDAC enforcement." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "deploymentSettingsType": { + "type": "object", + "properties": { + "deploymentPrefix": { + "type": "string", + "minLength": 4, + "maxLength": 8, + "metadata": { + "description": "Required. The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$." + } + }, + "clusterNodeNames": { + "type": "array", + "metadata": { + "description": "Required. Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]." + } + }, + "domainFqdn": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the Active Directory Domain Services. Example: \"contoso.com\"." + } + }, + "domainOUPath": { + "type": "string", + "metadata": { + "description": "Required. The ADDS OU path - ex \"OU=HCI,DC=contoso,DC=com\"." + } + }, + "hvciProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The Hypervisor-protected Code Integrity setting." + } + }, + "drtmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The hardware-dependent Secure Boot setting." + } + }, + "driftControlEnforced": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, the security baseline is re-applied regularly." + } + }, + "credentialGuardEnforced": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables the Credential Guard." + } + }, + "smbSigningEnforced": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, the SMB default instance requires sign in for the client and server services." + } + }, + "smbClusterEncryption": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, cluster east-west traffic is encrypted." + } + }, + "sideChannelMitigationEnforced": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, all the side channel mitigations are enabled." + } + }, + "bitlockerBootVolume": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent." + } + }, + "bitlockerDataVolumes": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes." + } + }, + "wdacEnforced": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Limits the applications and the code that you can run on your Azure Stack HCI cluster." + } + }, + "streamingDataClient": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The metrics data for deploying a HCI cluster." + } + }, + "isEuropeanUnionLocation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The location data for deploying a HCI cluster." + } + }, + "episodicDataUpload": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic data for deploying a HCI cluster." + } + }, + "storageConfigurationMode": { + "type": "string", + "allowedValues": [ + "Express", + "InfraOnly", + "KeepStorage" + ], + "nullable": true, + "metadata": { + "description": "Optional. The storage volume configuration mode. See documentation for details." + } + }, + "subnetMask": { + "type": "string", + "metadata": { + "description": "Required. The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0." + } + }, + "defaultGateway": { + "type": "string", + "metadata": { + "description": "Required. The default gateway of the Management Network. Example: 192.168.0.1." + } + }, + "startingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "endingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The DNS servers accessible from the Management Network for the HCI cluster." + } + }, + "networkIntents": { + "type": "array", + "metadata": { + "description": "Required. An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster." + } + }, + "storageConnectivitySwitchless": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether the Storage Network connectivity is switched or switchless." + } + }, + "enableStorageAutoIp": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false." + } + }, + "storageNetworks": { + "type": "array", + "metadata": { + "description": "Required. An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations." + } + }, + "customLocationName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01." + } + }, + "clusterWitnessStorageAccountName": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage account to be used as the witness for the HCI Windows Failover Cluster." + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster." + } + }, + "cloudId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource." + } + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 15, + "metadata": { + "description": "Required. The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "deploymentOperations": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [ + "Validate", + "Deploy" + ], + "allowedValues": [ + "Deploy", + "Validate" + ], + "metadata": { + "description": "Optional. The cluster deployment operations to execute. Defaults to \"[Validate, Deploy]\"." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deploymentSettings": { + "$ref": "#/definitions/deploymentSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The deployment settings of the cluster." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "useSharedKeyVault": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specify whether to use the shared key vault for the HCI cluster." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "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": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Azure Stack HCI Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bda0d508-adf1-4af0-9c28-88919fc3ae06')]", + "Windows Admin Center Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f')]" + }, + "sortedDeploymentOperations": "[if(not(empty(parameters('deploymentOperations'))), sort(parameters('deploymentOperations'), lambda('a', 'b', greater(lambdaVariables('a'), lambdaVariables('b')))), createArray())]" + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[take(format('46d3xbcp.res.azurestackhci-cluster.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cluster": { + "type": "Microsoft.AzureStackHCI/clusters", + "apiVersion": "2024-04-01", + "name": "[parameters('name')]", + "identity": { + "type": "SystemAssigned" + }, + "location": "[parameters('location')]", + "properties": {}, + "tags": "[parameters('tags')]" + }, + "cluster_roleAssignments": { + "copy": { + "name": "cluster_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.AzureStackHCI/clusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AzureStackHCI/clusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "cluster" + ] + }, + "deploymentSetting": { + "copy": { + "name": "deploymentSetting", + "count": "[length(variables('sortedDeploymentOperations'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[and(not(empty(variables('sortedDeploymentOperations')[copyIndex()])), not(empty(parameters('deploymentSettings'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploymentSettings-{0}', variables('sortedDeploymentOperations')[copyIndex()])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "cloudId": "[if(parameters('useSharedKeyVault'), createObject('value', reference('cluster').cloudId), createObject('value', null()))]", + "clusterName": { + "value": "[parameters('name')]" + }, + "deploymentMode": { + "value": "[variables('sortedDeploymentOperations')[copyIndex()]]" + }, + "clusterNodeNames": { + "value": "[parameters('deploymentSettings').clusterNodeNames]" + }, + "clusterWitnessStorageAccountName": { + "value": "[parameters('deploymentSettings').clusterWitnessStorageAccountName]" + }, + "customLocationName": { + "value": "[parameters('deploymentSettings').customLocationName]" + }, + "defaultGateway": { + "value": "[parameters('deploymentSettings').defaultGateway]" + }, + "deploymentPrefix": { + "value": "[parameters('deploymentSettings').deploymentPrefix]" + }, + "dnsServers": { + "value": "[parameters('deploymentSettings').dnsServers]" + }, + "domainFqdn": { + "value": "[parameters('deploymentSettings').domainFqdn]" + }, + "domainOUPath": { + "value": "[parameters('deploymentSettings').domainOUPath]" + }, + "endingIPAddress": { + "value": "[parameters('deploymentSettings').endingIPAddress]" + }, + "keyVaultName": { + "value": "[parameters('deploymentSettings').keyVaultName]" + }, + "networkIntents": { + "value": "[parameters('deploymentSettings').networkIntents]" + }, + "startingIPAddress": { + "value": "[parameters('deploymentSettings').startingIPAddress]" + }, + "storageConnectivitySwitchless": { + "value": "[parameters('deploymentSettings').storageConnectivitySwitchless]" + }, + "storageNetworks": { + "value": "[parameters('deploymentSettings').storageNetworks]" + }, + "subnetMask": { + "value": "[parameters('deploymentSettings').subnetMask]" + }, + "bitlockerBootVolume": { + "value": "[tryGet(parameters('deploymentSettings'), 'bitlockerBootVolume')]" + }, + "bitlockerDataVolumes": { + "value": "[tryGet(parameters('deploymentSettings'), 'bitlockerDataVolumes')]" + }, + "credentialGuardEnforced": { + "value": "[tryGet(parameters('deploymentSettings'), 'credentialGuardEnforced')]" + }, + "driftControlEnforced": { + "value": "[tryGet(parameters('deploymentSettings'), 'driftControlEnforced')]" + }, + "drtmProtection": { + "value": "[tryGet(parameters('deploymentSettings'), 'drtmProtection')]" + }, + "enableStorageAutoIp": { + "value": "[tryGet(parameters('deploymentSettings'), 'enableStorageAutoIp')]" + }, + "episodicDataUpload": { + "value": "[tryGet(parameters('deploymentSettings'), 'episodicDataUpload')]" + }, + "hvciProtection": { + "value": "[tryGet(parameters('deploymentSettings'), 'hvciProtection')]" + }, + "isEuropeanUnionLocation": { + "value": "[tryGet(parameters('deploymentSettings'), 'isRFEuropeanUnionLocation')]" + }, + "sideChannelMitigationEnforced": { + "value": "[tryGet(parameters('deploymentSettings'), 'sideChannelMitigationEnforced')]" + }, + "smbClusterEncryption": { + "value": "[tryGet(parameters('deploymentSettings'), 'smbClusterEncryption')]" + }, + "smbSigningEnforced": { + "value": "[tryGet(parameters('deploymentSettings'), 'smbSigningEnforced')]" + }, + "storageConfigurationMode": { + "value": "[tryGet(parameters('deploymentSettings'), 'storageConfigurationMode')]" + }, + "streamingDataClient": { + "value": "[tryGet(parameters('deploymentSettings'), 'streamingDataClient')]" + }, + "wdacEnforced": { + "value": "[tryGet(parameters('deploymentSettings'), 'wdacEnforced')]" + } + }, + "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.33.93.31351", + "templateHash": "7982158538530558379" + }, + "name": "Azure Stack HCI Cluster Deployment Settings", + "description": "This module deploys an Azure Stack HCI Cluster Deployment Settings resource." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. The name of the deployment settings." + } + }, + "clusterName": { + "type": "string", + "minLength": 4, + "maxLength": 15, + "metadata": { + "description": "Conditional. The name of the Azure Stack HCI cluster - this must be a valid Active Directory computer name and will be the name of your cluster in Azure. Required if the template is used in a standalone deployment." + } + }, + "deploymentMode": { + "type": "string", + "allowedValues": [ + "Validate", + "Deploy" + ], + "metadata": { + "description": "Required. First must pass with this parameter set to Validate prior running with it set to Deploy. If either Validation or Deployment phases fail, fix the issue, then resubmit the template with the same deploymentMode to retry." + } + }, + "deploymentPrefix": { + "type": "string", + "minLength": 4, + "maxLength": 8, + "metadata": { + "description": "Required. The prefix for the resource for the deployment. This value is used in key vault and storage account names in this template, as well as for the deploymentSettings.properties.deploymentConfiguration.scaleUnits.deploymentData.namingPrefix property which requires regex pattern: ^[a-zA-Z0-9-]{1,8}$." + } + }, + "clusterNodeNames": { + "type": "array", + "metadata": { + "description": "Required. Names of the cluster node Arc Machine resources. These are the name of the Arc Machine resources created when the new HCI nodes were Arc initialized. Example: [hci-node-1, hci-node-2]." + } + }, + "domainFqdn": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the Active Directory Domain Services. Example: \"contoso.com\"." + } + }, + "domainOUPath": { + "type": "string", + "metadata": { + "description": "Required. The ADDS OU path - ex \"OU=HCI,DC=contoso,DC=com\"." + } + }, + "hvciProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The Hypervisor-protected Code Integrity setting." + } + }, + "drtmProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The hardware-dependent Secure Boot setting." + } + }, + "driftControlEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, the security baseline is re-applied regularly." + } + }, + "credentialGuardEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables the Credential Guard." + } + }, + "smbSigningEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, the SMB default instance requires sign in for the client and server services." + } + }, + "smbClusterEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, cluster east-west traffic is encrypted." + } + }, + "sideChannelMitigationEnforced": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, all the side channel mitigations are enabled." + } + }, + "bitlockerBootVolume": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent." + } + }, + "bitlockerDataVolumes": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When set to true, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes." + } + }, + "wdacEnforced": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Limits the applications and the code that you can run on your Azure Stack HCI cluster." + } + }, + "streamingDataClient": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The metrics data for deploying a HCI cluster." + } + }, + "isEuropeanUnionLocation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The location data for deploying a HCI cluster." + } + }, + "episodicDataUpload": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The diagnostic data for deploying a HCI cluster." + } + }, + "storageConfigurationMode": { + "type": "string", + "defaultValue": "Express", + "allowedValues": [ + "Express", + "InfraOnly", + "KeepStorage" + ], + "metadata": { + "description": "Optional. The storage volume configuration mode. See documentation for details." + } + }, + "subnetMask": { + "type": "string", + "metadata": { + "description": "Required. The subnet mask pf the Management Network for the HCI cluster - ex: 255.255.252.0." + } + }, + "defaultGateway": { + "type": "string", + "metadata": { + "description": "Required. The default gateway of the Management Network. Example: 192.168.0.1." + } + }, + "startingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The starting IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "endingIPAddress": { + "type": "string", + "metadata": { + "description": "Required. The ending IP address for the Infrastructure Network IP pool. There must be at least 6 IPs between startingIPAddress and endingIPAddress and this pool should be not include the node IPs." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The DNS servers accessible from the Management Network for the HCI cluster." + } + }, + "networkIntents": { + "type": "array", + "metadata": { + "description": "Required. An array of Network ATC Network Intent objects that define the Compute, Management, and Storage network configuration for the cluster." + } + }, + "storageConnectivitySwitchless": { + "type": "bool", + "metadata": { + "description": "Required. Specify whether the Storage Network connectivity is switched or switchless." + } + }, + "enableStorageAutoIp": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable storage auto IP assignment. This should be true for most deployments except when deploying a three-node switchless cluster, in which case storage IPs should be configured before deployment and this value set to false." + } + }, + "storageNetworks": { + "type": "array", + "metadata": { + "description": "Required. An array of JSON objects that define the storage network configuration for the cluster. Each object should contain the adapterName, VLAN properties, and (optionally) IP configurations." + } + }, + "customLocationName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Custom Location associated with the Arc Resource Bridge for this cluster. This value should reflect the physical location and identifier of the HCI cluster. Example: cl-hci-den-clu01." + } + }, + "clusterWitnessStorageAccountName": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage account to be used as the witness for the HCI Windows Failover Cluster." + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the key vault to be used for storing secrets for the HCI cluster. This currently needs to be unique per HCI cluster." + } + }, + "cloudId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If using a shared key vault or non-legacy secret naming, pass the properties.cloudId guid from the pre-created HCI cluster resource." + } + } + }, + "variables": { + "copy": [ + { + "name": "arcNodeResourceIds", + "count": "[length(parameters('clusterNodeNames'))]", + "input": "[resourceId('Microsoft.HybridCompute/machines', parameters('clusterNodeNames')[copyIndex('arcNodeResourceIds')])]" + } + ] + }, + "resources": { + "cluster": { + "existing": true, + "type": "Microsoft.AzureStackHCI/clusters", + "apiVersion": "2024-04-01", + "name": "[parameters('clusterName')]" + }, + "deploymentSettings": { + "type": "Microsoft.AzureStackHCI/clusters/deploymentSettings", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": { + "arcNodeResourceIds": "[variables('arcNodeResourceIds')]", + "deploymentMode": "[parameters('deploymentMode')]", + "deploymentConfiguration": { + "version": "10.0.0.0", + "scaleUnits": [ + { + "deploymentData": { + "copy": [ + { + "name": "physicalNodes", + "count": "[length(variables('arcNodeResourceIds'))]", + "input": { + "name": "[reference(variables('arcNodeResourceIds')[copyIndex('physicalNodes')], '2022-12-27', 'Full').properties.displayName]", + "ipv4Address": "[filter(reference(format('{0}/providers/microsoft.azurestackhci/edgeDevices/default', variables('arcNodeResourceIds')[copyIndex('physicalNodes')]), '2024-01-01', 'Full').properties.deviceConfiguration.nicDetails, lambda('nic', not(equals(tryGet(lambdaVariables('nic'), 'defaultGateway'), null()))))[0].ip4Address]" + } + }, + { + "name": "secrets", + "count": "[length(createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey'))]", + "input": { + "secretName": "[if(empty(parameters('cloudId')), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], format('{0}-{1}-{2}', parameters('clusterName'), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], parameters('cloudId')))]", + "eceSecretName": "[createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')]]", + "secretLocation": "[if(empty(parameters('cloudId')), format('https://{0}{1}/secrets/{2}', parameters('keyVaultName'), environment().suffixes.keyvaultDns, createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')]), format('https://{0}{1}/secrets/{2}-{3}-{4}', parameters('keyVaultName'), environment().suffixes.keyvaultDns, parameters('clusterName'), createArray('LocalAdminCredential', 'AzureStackLCMUserCredential', 'DefaultARBApplication', 'WitnessStorageKey')[copyIndex('secrets')], parameters('cloudId')))]" + } + } + ], + "securitySettings": { + "hvciProtection": "[parameters('hvciProtection')]", + "drtmProtection": "[parameters('drtmProtection')]", + "driftControlEnforced": "[parameters('driftControlEnforced')]", + "credentialGuardEnforced": "[parameters('credentialGuardEnforced')]", + "smbSigningEnforced": "[parameters('smbSigningEnforced')]", + "smbClusterEncryption": "[parameters('smbClusterEncryption')]", + "sideChannelMitigationEnforced": "[parameters('sideChannelMitigationEnforced')]", + "bitlockerBootVolume": "[parameters('bitlockerBootVolume')]", + "bitlockerDataVolumes": "[parameters('bitlockerDataVolumes')]", + "wdacEnforced": "[parameters('wdacEnforced')]" + }, + "observability": { + "streamingDataClient": "[parameters('streamingDataClient')]", + "euLocation": "[parameters('isEuropeanUnionLocation')]", + "episodicDataUpload": "[parameters('episodicDataUpload')]" + }, + "cluster": { + "name": "[parameters('clusterName')]", + "witnessType": "Cloud", + "witnessPath": "", + "cloudAccountName": "[parameters('clusterWitnessStorageAccountName')]", + "azureServiceEndpoint": "[environment().suffixes.storage]" + }, + "storage": { + "configurationMode": "[parameters('storageConfigurationMode')]" + }, + "namingPrefix": "[parameters('deploymentPrefix')]", + "domainFqdn": "[parameters('domainFqdn')]", + "infrastructureNetwork": [ + { + "subnetMask": "[parameters('subnetMask')]", + "gateway": "[parameters('defaultGateway')]", + "ipPools": [ + { + "startingAddress": "[parameters('startingIPAddress')]", + "endingAddress": "[parameters('endingIPAddress')]" + } + ], + "dnsServers": "[parameters('dnsServers')]" + } + ], + "hostNetwork": { + "copy": [ + { + "name": "storageNetworks", + "count": "[length(parameters('storageNetworks'))]", + "input": { + "name": "[format('StorageNetwork{0}', add(copyIndex('storageNetworks'), 1))]", + "networkAdapterName": "[parameters('storageNetworks')[copyIndex('storageNetworks')].adapterName]", + "vlanId": "[parameters('storageNetworks')[copyIndex('storageNetworks')].vlan]", + "storageAdapterIPInfo": "[tryGet(parameters('storageNetworks')[copyIndex('storageNetworks')], 'storageAdapterIPInfo')]" + } + } + ], + "intents": "[parameters('networkIntents')]", + "storageConnectivitySwitchless": "[parameters('storageConnectivitySwitchless')]", + "enableStorageAutoIp": "[parameters('enableStorageAutoIp')]" + }, + "adouPath": "[parameters('domainOUPath')]", + "secretsLocation": "[format('https://{0}{1}', parameters('keyVaultName'), environment().suffixes.keyvaultDns)]", + "optionalServices": { + "customLocation": "[parameters('customLocationName')]" + } + } + } + ] + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cluster deployment settings." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the cluster deployment settings." + }, + "value": "[resourceId('Microsoft.AzureStackHCI/clusters/deploymentSettings', parameters('clusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the cluster deployment settings." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "cluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cluster." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the cluster." + }, + "value": "[resourceId('Microsoft.AzureStackHCI/clusters', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the cluster." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The managed identity of the cluster." + }, + "value": "[reference('cluster', '2024-04-01', 'full').identity.principalId]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the cluster." + }, + "value": "[reference('cluster', '2024-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciARBSPRoleAssignment.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciARBSPRoleAssignment.bicep new file mode 100644 index 0000000000..566aa85461 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciARBSPRoleAssignment.bicep @@ -0,0 +1,19 @@ +targetScope = 'subscription' + +@secure() +param arbDeploymentSPObjectId string + +var ARBDeploymentRoleID = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '7b1f81f9-4196-4058-8aae-762e593270df' +) + +resource ARBServicePrincipalResourceBridgeDeploymentRolePermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('ARBServicePrincipalResourceBridgeDeploymentRolePermissions', subscription().id, arbDeploymentSPObjectId) + properties: { + roleDefinitionId: ARBDeploymentRoleID + principalId: arbDeploymentSPObjectId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep new file mode 100644 index 0000000000..42248f3f1c --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep @@ -0,0 +1,346 @@ +param location string + +@description('Required. The name of the storage account to create as a cluster witness.') +param clusterWitnessStorageAccountName string + +@description('Required. The name of the storage account to be created to collect Key Vault diagnostic logs.') +param keyVaultDiagnosticStorageAccountName string + +@description('Required. The name of the Azure Key Vault to create.') +param keyVaultName string + +param softDeleteRetentionDays int = 30 + +@description('Optional. The number of days for the retention in days. A value of 0 will retain the events indefinitely.') +@minValue(0) +@maxValue(365) +param logsRetentionInDays int = 30 + +param tenantId string +@secure() +param hciResourceProviderObjectId string +param arcNodeResourceIds array +param deploymentUsername string = 'deployUser' +@secure() +param deploymentUserPassword string +param localAdminUsername string +@secure() +param localAdminPassword string +@secure() +param arbDeploymentAppId string +@secure() +param arbDeploymentSPObjectId string +@secure() +param arbDeploymentServicePrincipalSecret string +param vnetSubnetResourceId string? +param allowIPtoStorageAndKeyVault string? +param usingArcGW bool = false +param clusterName string? +param cloudId string? + +// secret names for the Azure Key Vault - these cannot be changed. +var localAdminSecretName = (empty(cloudId)) ? 'LocalAdminCredential' : '${clusterName}-LocalAdminCredential-${cloudId}' +var domainAdminSecretName = (empty(cloudId)) + ? 'AzureStackLCMUserCredential' + : '${clusterName}-AzureStackLCMUserCredential-${cloudId}' +var arbDeploymentServicePrincipalName = (empty(cloudId)) + ? 'DefaultARBApplication' + : '${clusterName}-DefaultARBApplication-${cloudId}' +var storageWitnessName = (empty(cloudId)) ? 'WitnessStorageKey' : '${clusterName}-WitnessStorageKey-${cloudId}' + +// create base64 encoded secret values to be stored in the Azure Key Vault +var deploymentUserSecretValue = base64('${deploymentUsername}:${deploymentUserPassword}') +var localAdminSecretValue = base64('${localAdminUsername}:${localAdminPassword}') +var arbDeploymentServicePrincipalValue = base64('${arbDeploymentAppId}:${arbDeploymentServicePrincipalSecret}') + +var storageAccountType = 'Standard_ZRS' + +var azureConnectedMachineResourceManagerRoleID = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f5819b54-e033-4d82-ac66-4fec3cbf3f4c' +) +var readerRoleID = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' +) +var azureStackHCIDeviceManagementRole = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '865ae368-6a45-4bd1-8fbf-0d5151f56fc1' +) +var keyVaultSecretUserRoleID = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4633458b-17de-408a-b874-0445c86b69e6' +) + +module ARBDeploymentSPNSubscriptionRoleAssignmnent 'ashciARBSPRoleAssignment.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-arbroleassignment' + scope: subscription() + params: { + arbDeploymentSPObjectId: arbDeploymentSPObjectId + } +} + +resource diagnosticStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: keyVaultDiagnosticStorageAccountName + location: location + sku: { + name: storageAccountType + } + kind: 'StorageV2' + properties: { + supportsHttpsTrafficOnly: true + allowBlobPublicAccess: false + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [] + virtualNetworkRules: [] + } + } + resource blobService 'blobServices' = { + name: 'default' + properties: { + deleteRetentionPolicy: { + enabled: true + days: 7 + } + containerDeleteRetentionPolicy: { + enabled: true + days: 7 + } + } + } +} + +resource witnessStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: clusterWitnessStorageAccountName + location: location + sku: { + name: storageAccountType + } + kind: 'StorageV2' + properties: { + supportsHttpsTrafficOnly: true + allowBlobPublicAccess: false + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + defaultAction: usingArcGW ? 'Allow' : 'Deny' // we don't know the source IP when traffic is redirected through the Arc GW + ipRules: (allowIPtoStorageAndKeyVault != null) + ? [ + { + value: allowIPtoStorageAndKeyVault! + } + ] + : [] + virtualNetworkRules: (vnetSubnetResourceId != null) + ? [ + { + id: vnetSubnetResourceId! + } + ] + : [] + } + } + resource blobService 'blobServices' = { + name: 'default' + properties: { + deleteRetentionPolicy: { + enabled: true + days: 7 + } + containerDeleteRetentionPolicy: { + enabled: true + days: 7 + } + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: keyVaultName + location: location + properties: { + enabledForDeployment: true + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enableSoftDelete: true + softDeleteRetentionInDays: softDeleteRetentionDays + enableRbacAuthorization: true + publicNetworkAccess: 'Enabled' + accessPolicies: [] + tenantId: tenantId + sku: { + name: 'standard' + family: 'A' + } + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: (allowIPtoStorageAndKeyVault != null) + ? [ + { + value: allowIPtoStorageAndKeyVault! + } + ] + : [] + virtualNetworkRules: (vnetSubnetResourceId != null) + ? [ + { + id: vnetSubnetResourceId! + } + ] + : [] + } + } + dependsOn: [ + diagnosticStorageAccount + ] + + resource keyVaultName_domainAdminSecret 'secrets@2023-07-01' = { + name: domainAdminSecretName + properties: { + contentType: 'Secret' + value: deploymentUserSecretValue + attributes: { + enabled: true + } + } + } + + resource keyVaultName_localAdminSecret 'secrets@2023-07-01' = { + name: localAdminSecretName + properties: { + contentType: 'Secret' + value: localAdminSecretValue + attributes: { + enabled: true + } + } + } + + resource keyVaultName_arbDeploymentServicePrincipal 'secrets@2023-07-01' = { + name: arbDeploymentServicePrincipalName + properties: { + contentType: 'Secret' + value: arbDeploymentServicePrincipalValue + attributes: { + enabled: true + } + } + } + + resource keyVaultName_storageWitness 'secrets@2023-07-01' = { + name: storageWitnessName + properties: { + contentType: 'Secret' + value: base64(witnessStorageAccount.listKeys().keys[0].value) + attributes: { + enabled: true + } + } + } +} + +resource keyVaultName_Microsoft_Insights_service 'microsoft.insights/diagnosticSettings@2016-09-01' = { + name: 'service' + location: location + scope: keyVault + properties: { + storageAccountId: diagnosticStorageAccount.id + logs: [ + { + category: 'AuditEvent' + enabled: true + retentionPolicy: { + enabled: true + days: logsRetentionInDays + } + } + ] + } +} + +resource SPConnectedMachineResourceManagerRolePermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid( + subscription().subscriptionId, + hciResourceProviderObjectId, + 'ConnectedMachineResourceManagerRolePermissions', + resourceGroup().id + ) + scope: resourceGroup() + properties: { + roleDefinitionId: azureConnectedMachineResourceManagerRoleID + principalId: hciResourceProviderObjectId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } +} + +resource NodeAzureConnectedMachineResourceManagerRolePermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for hciNode in arcNodeResourceIds: { + name: guid( + subscription().subscriptionId, + hciResourceProviderObjectId, + 'azureConnectedMachineResourceManager', + hciNode, + resourceGroup().id + ) + properties: { + roleDefinitionId: azureConnectedMachineResourceManagerRoleID + principalId: reference(hciNode, '2023-10-03-preview', 'Full').identity.principalId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } + } +] + +resource NodeazureStackHCIDeviceManagementRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for hciNode in arcNodeResourceIds: { + name: guid( + subscription().subscriptionId, + hciResourceProviderObjectId, + 'azureStackHCIDeviceManagementRole', + hciNode, + resourceGroup().id + ) + properties: { + roleDefinitionId: azureStackHCIDeviceManagementRole + principalId: reference(hciNode, '2023-10-03-preview', 'Full').identity.principalId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } + } +] + +resource NodereaderRoleIDPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for hciNode in arcNodeResourceIds: { + name: guid(subscription().subscriptionId, hciResourceProviderObjectId, 'reader', hciNode, resourceGroup().id) + properties: { + roleDefinitionId: readerRoleID + principalId: reference(hciNode, '2023-10-03-preview', 'Full').identity.principalId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } + } +] + +resource KeyVaultSecretsUserPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for hciNode in arcNodeResourceIds: { + name: guid( + subscription().subscriptionId, + hciResourceProviderObjectId, + 'keyVaultSecretUser', + hciNode, + resourceGroup().id + ) + scope: keyVault + properties: { + roleDefinitionId: keyVaultSecretUserRoleID + principalId: reference(hciNode, '2023-10-03-preview', 'Full').identity.principalId + principalType: 'ServicePrincipal' + description: 'Created by Azure Stack HCI deployment template' + } + } +] diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/README.md b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/README.md new file mode 100644 index 0000000000..af59eeeb48 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/README.md @@ -0,0 +1,67 @@ +# HCI Assets for deployment validation + +The templates & scripts in this folder are designed to support the testing of Azure Stack HCI scenarios using nested virtualization on a VM deployed in Azure. + +The module creates an Azure VM, then uses a series of PowerShell Managed Run Commands and Deployment Scripts to prepare the host VM and deploy the nested Azure Stack HCI VMs. To simplify the design, the host VM is also configured as an AD domain controller, DNS server, DHCP server, and RRAS router, in addition to a Hyper-v host. The deployment is zero-touch to support pipeline deployments. + +The HCI Node VMs are brought to the point of Arc initialization. Deployment of the Azure Stack HCI cluster on top of the node VMs requires additional steps, either taken through the Portal or another IaC template. + +## Capabilities + +- HCI Cluster Size: The number of Azure Stack HCI nodes is determined by the `hciNodeCount` parameter. For each node, an additional data disk is added to the host VM. The host VM must be sized appropriately to accommodate the count of nodes (RAM and CPUs), but the solution is best suited for smaller clusters (1-4 nodes) +- Spot VM: The host VM can be configured as a spot VM to reduce cost; however depending on the evication rate of the chosen spot SKU, this may be problematic--for example, if the VM is evicted during deployment of the HCI cluster. +- Switched and switchless support: the module supports switched and switchless cluster designs with the `switchlessStorageConfig` parameter +- Idempotence: The deployment has been written to be idempotent in most scenarios, so re-running after fixing an issue with a failed deployment is valid. +- Existing VNET association: the host VM can be connected to an existing VNET by passing the `vnetSubnetID` parameter--which can help with troubleshooting; otherwise, a new VNET is created. +- VHDX or ISO HCI OS source: the HCI node VMs are built with the Azure Stack HCI OS installed. The OS can be sourced from either a VHDX or an ISO file using either the `hciVHDXDownloadURL` or `hciISODownloadURL` parameters. +- Deployment with a proxy and/or Arc Gateway + +## Troubleshooting + +### Credentials + +By default, the Domain Admin account and local administrator accounts have the username `admin-hci` and the password for these accounts is set by the `localAdminPassword` parameter. If you pass a random value to this parameter, but later need the password for troubleshooting a deployment issue, here are a couple options: + +- Use a Run Command on the host VM to reset the admin password. Ex: `net user admin-hci ''` +- Use a Run Command to decrypt the exported password stored locally on the host VM. Note, this password is encrypted to the SYSTEM account, so you must use Run Command or a tool like PSExec to decrypt it. Ex: + + ```powershell + $c = Import-CliXML -Path 'C:\temp\hciHostDeployAdminCred.xml' + $c.GetNetworkCredential().password + ``` + +### Logging + +All Run Command scripts log activity to the `C:\temp\hciHostDeploy.log` path on the host VM. + +It is also possible to review Run Command output with a REST call, making sure you expand the instance view. For example: `invoke-azrest -path '/subscriptions//resourceGroups//providers/Microsoft.Compute/virtualMachines/hciHost01/runCommands/runCommand6?api-version=2024-07-01&$expand=instanceView'` + +## Using this module for a host through Arc initialization-only deployment + +To use this Bicep module, you'll need to download a copy then supply values for the required parameters. Follow these steps: + +1. Clone the repository with Git: `git clone https://github.com/Azure/bicep-registry-modules.git` +1. Navigate to this directory `avm\utilities\e2e-template-assets\templates\azure-stack-hci\modules\azureStackHCIHost` and review the parameters at the top of the `hciHostDeployment.bicep` file. Provide values as required by hardcoding them into the Bicep file or by building a parameters file. +1. Run the Bicep deployment. For example: `az deployment group create -f .\avm\utilities\e2e-template-assets\templates\azure-stack-hci\modules\azureStackHCIHost\hciHostDeployment.bicep` +1. When the deployment completes, the HCI nodes will be ready for an HCI cluster to be deployed on them. For example, you could use the Bicep module at this path `avm\res\azure-stack-hci\cluster\main.bicep` or using the Azure Portal. + +## Using this template for an end-to-end HCI cluster deployment in Azure + +To use this Bicep module, you'll need to download a copy then supply values for the required parameters. Follow these steps: + +1. Clone the repository with Git: `git clone https://github.com/Azure/bicep-registry-modules.git` +1. Pick one of the E2E test templates in `avm\res\azure-stack-hci\cluster\tests\e2e`. NOTE: these templates all deploy to 'southeastasia' - to use another location, modify the `enforcedLocation` variable. +1. Create a parameter object in PowerShell to pass to the deployment. For example: + + ```powershell + $templateParameterObjectAIRS = @{ + 'arbDeploymentServicePrincipalSecret' = (Read-Host -AsSecureString -Prompt 'arbDeploymentServicePrincipalSecret') + 'localAdminAndDeploymentUserPass' = (Read-Host -AsSecureString -Prompt 'localAdminAndDeploymentUserPass') + 'arbDeploymentSPObjectId' = '4aceef22-f89f-42e8-8d8b-5aa3fb8fb57b' + 'arbDeploymentAppId' = '6e9515aa-715d-4a18-bdfe-463e8a004ffe' + 'hciResourceProviderObjectId' = 'e44d5f5e-21c6-4b3a-b697-e1236d00b006' + 'location' = 'southeastasia' + 'deploymentPrefix' = -join ((97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + } + ``` +1. Execute the deployment. Example: `new-AzSubscriptionDeployment -TemplateFile C:\Users\mbratschun\repos\bicep-registry-modules\avm\res\azure-stack-hci\cluster\tests\e2e\2nodeswitched.defaults\main.test.bicep -TemplateParameterObject $templateParameterObjectBAMI24H2 -Location southeastasia` diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep new file mode 100644 index 0000000000..d9d9e50a83 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep @@ -0,0 +1,535 @@ +@description('Required. The location for all resource except HCI Arc Nodes and HCI resources') +param location string + +@description('Optional. The Azure VM size for the HCI Host VM, which must support nested virtualization and have sufficient capacity for the HCI node VMs!') +param hostVMSize string = 'Standard_E32bds_v5' + +@description('Optional. The number of Azure Stack HCI nodes to deploy.') +param hciNodeCount int = 2 + +@description('Optional. Enable configuring switchless storage.') +param switchlessStorageConfig bool = false + +@description('Optional. The download URL for the Azure Stack HCI ISO.') +param hciISODownloadURL string = 'https://azurestackreleases.download.prss.microsoft.com/dbazure/AzureStackHCI/OS-Composition/10.2408.0.3061/AZURESTACKHci23H2.25398.469.LCM.10.2408.0.3061.x64.en-us.iso' + +@description('Optional. The local admin user name.') +param localAdminUsername string = 'admin-hci' + +@description('Required. The local admin password.') +@secure() +param localAdminPassword string + +@description('Optional. The domain OU path.') +param domainOUPath string = 'OU=HCI,DC=HCI,DC=local' + +@description('Optional. The deployment username.') +param deploymentUsername string = 'deployUser' + +@description('Required. The name of the VM-managed user identity to create, used for HCI Arc onboarding.') +param userAssignedIdentityName string + +@description('Required. The name of the VNET for the HCI host Azure VM.') +param virtualNetworkName string + +@description('Required. The name of the NSG to create.') +param networkSecurityGroupName string + +@description('Required. The name of the maintenance configuration for the Azure Stack HCI Host VM and proxy server.') +param maintenanceConfigurationName string + +@description('Required. The name of the Azure VM scale set for the HCI host.') +param HCIHostVirtualMachineScaleSetName string + +@description('Required. The name of the Network Interface Card to create.') +param networkInterfaceName string + +@description('Required. The name prefix for the Disks to create.') +param diskNamePrefix string + +@description('Required. The name of the Azure VM to create.') +param virtualMachineName string + +@description('Required. The name of the Maintenance Configuration Assignment for the proxy server.') +param maintenanceConfigurationAssignmentName string + +@description('Required. The name prefix for the \'wait\' deployment scripts to create.') +param waitDeploymentScriptPrefixName string + +// =================================// +// Deploy Host VM Infrastructure // +// =================================// + +// vm managed identity used for HCI Arc onboarding +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = { + location: location + name: userAssignedIdentityName +} + +// grant identity owner permissions on the resource group +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().subscriptionId, userAssignedIdentity.name, 'Owner', resourceGroup().id) + properties: { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + principalType: 'ServicePrincipal' + description: 'Role assigned used for Azure Stack HCI IaC testing pipeline - remove if identity no longer exists!' + } +} + +// grant identity contributor permissions on the subscription - needed to register resource providers +module roleAssignment_subscriptionContributor 'modules/subscriptionRoleAssignment.bicep' = { + name: '${uniqueString(deployment().name, location)}-hcihostmi-roleAssignment_subContributor' + scope: subscription() + params: { + principalId: userAssignedIdentity.properties.principalId + } +} + +// optional VNET and subnet for the HCI host Azure VM +resource vnet 'Microsoft.Network/virtualNetworks@2020-11-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: ['10.0.0.0/24'] + } + subnets: [ + { + name: 'subnet01' + properties: { + addressPrefix: '10.0.0.0/24' + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + locations: [location] + } + { + service: 'Microsoft.KeyVault' + locations: [location] + } + ] + } + } + ] + } +} + +// create a mintenance configuration for the Azure Stack HCI Host VM and proxy server +resource maintenanceConfig 'Microsoft.Maintenance/maintenanceConfigurations@2023-09-01-preview' = { + location: location + name: maintenanceConfigurationName ?? '' + properties: { + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + recurEvery: 'Week Sunday' + startDateTime: '2020-04-30 08:00' + duration: '02:00' + timeZone: 'UTC' + } + installPatches: { + windowsParameters: { + classificationsToInclude: ['Critical', 'Security'] + } + rebootSetting: 'IfRequired' + } + extensionProperties: { + InGuestPatchMode: 'User' + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2020-11-01' = { + location: location + name: networkSecurityGroupName +} + +resource hciHostVMSSFlex 'Microsoft.Compute/virtualMachineScaleSets@2024-03-01' = { + name: HCIHostVirtualMachineScaleSetName + location: location + zones: ['1', '2', '3'] + properties: { + orchestrationMode: 'Flexible' + platformFaultDomainCount: 1 + } +} + +resource nic 'Microsoft.Network/networkInterfaces@2020-11-01' = { + location: location + name: networkInterfaceName + properties: { + networkSecurityGroup: { + id: networkSecurityGroup.id + } + ipConfigurations: [ + { + name: 'ipConfig01' + properties: { + subnet: { + id: vnet.properties.subnets[0].id + } + privateIPAllocationMethod: 'Dynamic' + } + } + ] + } +} + +// host VM disks +resource disks 'Microsoft.Compute/disks@2023-10-02' = [ + for diskNum in range(1, hciNodeCount): { + name: '${diskNamePrefix}${string(diskNum)}' + location: location + zones: ['1'] + sku: { + name: 'Premium_LRS' + } + properties: { + diskSizeGB: 2048 + networkAccessPolicy: 'DenyAll' + creationData: { + createOption: 'Empty' + } + } + } +] + +// Azure Stack HCI Host VM - +resource vm 'Microsoft.Compute/virtualMachines@2024-03-01' = { + location: location + name: virtualMachineName + zones: ['1'] + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${userAssignedIdentity.id}': {} + } + } + properties: { + virtualMachineScaleSet: { + id: hciHostVMSSFlex.id + } + hardwareProfile: { + vmSize: hostVMSize + } + priority: 'Regular' + networkProfile: { + networkInterfaces: [ + { + id: nic.id + } + ] + } + storageProfile: { + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: '2022-datacenter-g2' + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + diskSizeGB: 128 + deleteOption: 'Delete' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + dataDisks: [ + for diskNum in range(1, hciNodeCount): { + lun: diskNum + createOption: 'Attach' + caching: 'ReadOnly' + managedDisk: { + id: disks[diskNum - 1].id + } + deleteOption: 'Delete' + } + ] + //diskControllerType: 'NVMe' + } + osProfile: { + adminPassword: localAdminPassword + adminUsername: localAdminUsername + computerName: 'hciHost01' + windowsConfiguration: { + provisionVMAgent: true + enableAutomaticUpdates: true + patchSettings: { + patchMode: 'AutomaticByPlatform' + automaticByPlatformSettings: { + bypassPlatformSafetyChecksOnUserSchedule: true + } + } + } + } + securityProfile: { + uefiSettings: { + secureBootEnabled: true + vTpmEnabled: true + } + securityType: 'TrustedLaunch' + } + licenseType: 'Windows_Server' + } +} + +resource maintenanceAssignment_hciHost 'Microsoft.Maintenance/configurationAssignments@2023-04-01' = { + location: location + name: maintenanceConfigurationAssignmentName + properties: { + maintenanceConfigurationId: maintenanceConfig.id + } + scope: vm +} + +// ====================// +// Install Host Roles // +// ====================// + +// installs roles and features required for Azure Stack HCI Host VM +resource runCommand1 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand1' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage1.ps1') + } + treatFailureAsDeploymentFailure: true + } +} + +// schedules a reboot of the VM +resource runCommand2 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand2' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage2.ps1') + } + treatFailureAsDeploymentFailure: true + } + dependsOn: [runCommand1] +} + +// initiates a wait for the VM to reboot +resource wait1 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + location: location + kind: 'AzurePowerShell' + name: '${waitDeploymentScriptPrefixName}-wait1' + properties: { + azPowerShellVersion: '3.0' + scriptContent: 'Start-Sleep -Seconds 90' + retentionInterval: 'PT6H' + } + dependsOn: [runCommand2] +} + +// ======================// +// Configure Host Roles // +// ======================// + +// initializes and mounts data disks, downloads HCI VHDX, configures the Azure Stack HCI Host VM with AD, routing, DNS, DHCP +resource runCommand3 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand3' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage3.ps1') + } + parameters: [ + { + name: 'hciVHDXDownloadURL' + value: '' + } + { + name: 'hciISODownloadURL' + value: hciISODownloadURL + } + { + name: 'hciNodeCount' + value: string(hciNodeCount) + } + ] + treatFailureAsDeploymentFailure: true + } + dependsOn: [wait1] +} + +// schedules a reboot of the VM +resource runCommand4 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand4' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage4.ps1') + } + treatFailureAsDeploymentFailure: true + } + dependsOn: [runCommand3] +} + +// initiates a wait for the VM to reboot - extra time for AD initialization +resource wait2 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + location: location + kind: 'AzurePowerShell' + name: '${waitDeploymentScriptPrefixName}-wait2' + properties: { + azPowerShellVersion: '3.0' + scriptContent: 'Start-Sleep -Seconds 300 #enough time for AD start-up' + retentionInterval: 'PT6H' + } + dependsOn: [ + runCommand4 + ] +} + +// ===========================// +// Create HCI Node Guest VMs // +// ===========================// + +// creates hyper-v resources, configures NAT, builds and preps the Azure Stack HCI node VMs +resource runCommand5 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand5' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage5.ps1') + } + parameters: [ + { + name: 'adminUsername' + value: localAdminUsername + } + { + name: 'hciNodeCount' + value: string(hciNodeCount) + } + { + name: 'switchlessStorageConfig' + value: switchlessStorageConfig ? 'switchless' : 'switched' + } + ] + protectedParameters: [ + { + name: 'adminPw' + value: localAdminPassword + } + ] + treatFailureAsDeploymentFailure: true + } + dependsOn: [wait2] +} + +// ================================================// +// Initialize Arc on HCI Node VMs and AD for HCI // +// ==============================================// + +// prepares AD for ASHCI onboarding, initiates Arc onboarding of HCI node VMs +resource runCommand6 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand6' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage6.ps1') + } + parameters: [ + { + name: 'location' + value: location + } + { + name: 'resourceGroupName' + value: resourceGroup().name + } + { + name: 'subscriptionId' + value: subscription().subscriptionId + } + { + name: 'tenantId' + value: tenant().tenantId + } + { + name: 'accountName' + value: userAssignedIdentity.properties.principalId + } + { + name: 'adminUsername' + value: localAdminUsername + } + { + name: 'arcGatewayId' + value: '' + } + { + name: 'deploymentUsername' + value: deploymentUsername + } + { + name: 'domainOUPath' + value: domainOUPath + } + { + name: 'proxyBypassString' + value: '' + } + { + name: 'proxyServerEndpoint' + value: '' + } + { + name: 'userAssignedManagedIdentityClientId' + value: userAssignedIdentity.properties.clientId + } + ] + protectedParameters: [ + { + name: 'adminPw' + value: localAdminPassword + } + ] + treatFailureAsDeploymentFailure: true + } + dependsOn: [runCommand5] +} + +// waits for HCI extensions to be in succeeded state +resource runCommand7 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = { + parent: vm + location: location + name: 'runCommand7' + properties: { + source: { + script: loadTextContent('./scripts/hciHostStage7.ps1') + } + parameters: [ + { + name: 'hciNodeCount' + value: string(hciNodeCount) + } + { + name: 'resourceGroupName' + value: resourceGroup().name + } + { + name: 'subscriptionId' + value: subscription().subscriptionId + } + { + name: 'userAssignedManagedIdentityClientId' + value: userAssignedIdentity.properties.clientId + } + ] + treatFailureAsDeploymentFailure: true + } + dependsOn: [runCommand6] +} + +output vnetSubnetResourceId string = vnet.properties.subnets[0].id diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/modules/subscriptionRoleAssignment.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/modules/subscriptionRoleAssignment.bicep new file mode 100644 index 0000000000..1493a632c5 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/modules/subscriptionRoleAssignment.bicep @@ -0,0 +1,16 @@ +targetScope = 'subscription' + +param principalId string + +// assign principalId the contributor role on the subscription +resource subscriptionRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(principalId, subscription().id, 'Contributor') + properties: { + principalId: principalId + principalType: 'ServicePrincipal' + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'b24988ac-6180-42a0-ab88-20f7382dd24c' + ) + } +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage1.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage1.ps1 new file mode 100644 index 0000000000..d62fea3953 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage1.ps1 @@ -0,0 +1,53 @@ +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-1.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage1] - $message" +} + +$ErrorActionPreference = 'Stop' + +# prep host - install hyper-v, AD, DHCP, RRAS +log 'Installing required features and roles...' +$features = @('rsat-hyper-v-tools', 'rsat-clustering', 'rsat-adds', 'rsat-dns-server', 'RSAT-RemoteAccess-Mgmt', 'Routing', 'AD-Domain-Services', 'DHCP') +$missingFeatures = Get-WindowsFeature -Name $features | Where-Object { $_.Installed -eq $false } + +ForEach ($missingFeature in $missingFeatures) { + log "Installing $($missingFeature.Name)..." + Add-WindowsFeature -Name $missingFeature.Name -IncludeAllSubFeature -IncludeManagementTools + + If ($?) { + log "Successfully installed $($missingFeature.Name)" + } Else { + log "Failed to install $($missingFeature.Name)" + } + If (Test-Path -Path C:\Windows\winsxs\pending.xml) { + log 'Reboot required, exiting...' + } +} + +log 'Enabling Hyper-V...' +Enable-WindowsOptionalFeature -Online -FeatureName 'microsoft-hyper-v-online' -All -NoRestart + +# create temp directory +log 'Creating temp directory...' +If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory +} + +# create reboot status file +If (Test-Path -Path 'C:\temp\Reboot1Completed.status') { + log 'Reboot has already been completed, skipping...' +} ElseIf (Test-Path -Path 'C:\temp\Reboot1Initiated.status') { + log 'Reboot has already been initiated, skipping...' +} Else { + log 'Reboot required, creating status file...' + Set-Content -Path 'C:\temp\Reboot1Required.status' -Value 'Reboot 1 Required' +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage2.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage2.ps1 new file mode 100644 index 0000000000..cf78579414 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage2.ps1 @@ -0,0 +1,40 @@ +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-2.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage2] - $message" +} + +$ErrorActionPreference = 'Stop' + +# check for reboot status file, reboot if needed +If (Test-Path -Path 'C:\temp\Reboot1Required.status') { + log 'Reboot 1 is required' + + Remove-Item 'C:\temp\Reboot1Required.status' + Set-Content -Path 'C:\temp\Reboot1Initiated.status' -Value 'Reboot 1 Initiated' + + # use scheduled task to reboot the machine, ensuring the runCommand exits gracefully + $action = New-ScheduledTaskAction -Execute 'shutdown.exe' -Argument '-r -f -t 0' + $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(2) + $principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -LogonType ServiceAccount + $task = New-ScheduledTask -Action $action -Description 'Reboot 1' -Trigger $trigger -Principal $principal + Register-ScheduledTask -TaskName 'Reboot1' -InputObject $task +} ElseIf (Test-Path -Path 'C:\temp\Reboot1Initiated.status') { + log 'Reboot 1 has been initiated and now completed' + + Remove-Item 'C:\temp\Reboot1Initiated.status' + Set-Content -Path 'C:\temp\Reboot1Completed.status' -Value 'Reboot 1 Completed' + + +} ElseIf (Test-Path -Path 'C:\temp\Reboot1Completed.status') { + log 'Reboot 1 has been completed' + +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage3.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage3.ps1 new file mode 100644 index 0000000000..e10b790976 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage3.ps1 @@ -0,0 +1,333 @@ +[CmdletBinding()] +param ( + [Parameter()] + [string] + $hciVHDXDownloadURL, + + [Parameter()] + [string] + $hciISODownloadURL, + + [Parameter()] + [ValidateRange(1, 16)] + [int] + $hciNodeCount +) +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-3.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage3] - $message" +} + +Function Test-ADConnection { + try { + If ((Get-Service -Name 'ADWS' -ErrorAction SilentlyContinue).Status -ne 'Running') { return $false } + $env:ADPS_LoadDefaultDrive = 0 + Import-Module -Name ActiveDirectory -ErrorAction Stop + [bool](Get-ADDomainController -Server $env:COMPUTERNAME -ErrorAction SilentlyContinue) + } catch { + $false + } +} + +# THANKS https://github.com/bfrankMS/CreateHypervVms/blob/master/Scenario-AzStackHCI/CreateVhdxFromIso.ps1 +Function New-VHDXFromISO { + # Parameter help description + param( + [Parameter(ParameterSetName = 'SRC', Mandatory = $true, ValueFromPipeline = $true)] + [Alias('ISO')] + [string] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $IsoPath, + + [Parameter(ParameterSetName = 'SRC')] + [Alias('VHD')] + [string] + [ValidateNotNullOrEmpty()] + $VhdxPath, + + [Parameter(ParameterSetName = 'SRC')] + [Alias('Size')] + [UInt64] + [ValidateNotNullOrEmpty()] + [ValidateRange(512MB, 64TB)] + $SizeBytes = 120GB, + + [Parameter(ParameterSetName = 'SRC')] + [Alias('Index')] + [UInt64] + [ValidateNotNullOrEmpty()] + [ValidateRange(1, 10)] + $ImageIndex = 1 #defaults to azure stack hci on 2405 image + ) + + $BCDBoot = 'bcdboot.exe' + $VHDFormat = 'VHDX' + $TempDirectory = $env:Temp + + function + Write-ActionInfo { + # Function to make the Write-Host output a bit prettier. + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string] + [ValidateNotNullOrEmpty()] + $text + ) + Write-Host "INFO : $($text)" -ForegroundColor White + } + + function + Start-Executable { + <# + .SYNOPSIS + Runs an external executable file, and validates the error level. + + .PARAMETER Executable + The path to the executable to run and monitor. + + .PARAMETER Arguments + An array of arguments to pass to the executable when it's executed. + + .PARAMETER SuccessfulErrorCode + The error code that means the executable ran successfully. + The default value is 0. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + [ValidateNotNullOrEmpty()] + $Executable, + + [Parameter(Mandatory = $true)] + [string[]] + [ValidateNotNullOrEmpty()] + $Arguments, + + [Parameter()] + [int] + [ValidateNotNullOrEmpty()] + $SuccessfulErrorCode = 0 + + ) + + Write-ActionInfo "Running $Executable $Arguments" + $ret = Start-Process ` + -FilePath $Executable ` + -ArgumentList $Arguments ` + -NoNewWindow ` + -Wait ` + -RedirectStandardOutput "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardOutput.txt" ` + -RedirectStandardError "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardError.txt" ` + -PassThru + + Write-ActionInfo "Return code was $($ret.ExitCode)." + + if ($ret.ExitCode -ne $SuccessfulErrorCode) { + throw "$Executable failed with code $($ret.ExitCode)!" + } + } + + Write-ActionInfo 'Creating virtual hard disk...' + $newVhd = New-VHD -Path $VhdxPath -SizeBytes $SizeBytes -Dynamic + + Write-ActionInfo "Mounting $VHDFormat..." + $disk = $newVhd | Mount-VHD -Passthru | Get-Disk + + + # UEFI : 3 partitions : efi - msr - windows + <#https://learn.microsoft.com/de-de/windows/win32/api/winioctl/ns-winioctl-partition_information_gpt +PARTITION_BASIC_DATA_GUID - ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 +PARTITION_SYSTEM_GUID - c12a7328-f81f-11d2-ba4b-00a0c93ec93b # EFI +PARTITION_MSFT_RESERVED_GUID - e3c9e316-0b5c-4db8-817d-f92df00215ae # MSR +PARTITION_MSFT_RECOVERY_GUID - de94bba4-06d1-4d40-a16a-bfd50179d6ac # Recovery partition (Windows) we are not using this +#> + + try { + Write-ActionInfo 'Initializing disk...' + Initialize-Disk -Number $disk.Number -PartitionStyle GPT + + Write-ActionInfo 'Creating EFI system partition...' + $systemPartition = New-Partition -DiskNumber $disk.Number -Size 200MB -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' + + Write-ActionInfo 'Formatting EFI system volume...' + $null = Format-Volume -Partition $systemPartition -FileSystem FAT32 -Force -Confirm:$false + + Write-ActionInfo 'Setting EFI system partition PARTITION_SYSTEM_GUID...' + $systemPartition | Set-Partition -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' + $systemPartition | Add-PartitionAccessPath -AssignDriveLetter + + # Create the reserved partition + Write-ActionInfo 'Creating MSR partition...' + $null = New-Partition -DiskNumber $disk.Number -Size 128MB -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}' -Verbose + + # Create the Windows partition + Write-ActionInfo 'Creating windows partition...' + $windowsPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' + + Write-ActionInfo 'Formatting windows volume...' + $windowsVolume = Format-Volume -Partition $windowsPartition -FileSystem NTFS -Force -Confirm:$false + + # Assign drive letter to Windows partition. This is required for bcdboot + $windowsPartition | Add-PartitionAccessPath -AssignDriveLetter + $windowsDrive = $(Get-Partition -Volume $windowsVolume).AccessPaths[0].substring(0, 2) + Write-ActionInfo "Windows path ($windowsDrive) has been assigned." + + # Refresh access paths (we have now formatted the volume) + $systemPartition = $systemPartition | Get-Partition + $systemDrive = $systemPartition.AccessPaths[0].trimend('\').replace('\?', '??') + Write-ActionInfo "System volume location: $systemDrive" + + # Mount .iso to get the install.wim + $beforeMount = (Get-Volume).DriveLetter -split ' ' + $null = Mount-DiskImage -StorageType ISO -ImagePath $IsoPath + $afterMount = (Get-Volume).DriveLetter -split ' ' + $setuppath = (Compare-Object $beforeMount $afterMount -PassThru ) + Write-ActionInfo "Mounted .iso to $($setuppath):" + + Write-ActionInfo "Applying image from .iso $("$setuppath"+':\sources\install.wim') to $VHDFormat. This could take a while..." + + Expand-WindowsImage -ApplyPath $windowsDrive -ImagePath "$setuppath`:\sources\install.wim" -Index $ImageIndex #-LogPath "$($logFolder)\DismLogs.log" | Out-Null + Write-ActionInfo 'Image was applied successfully. ' + + Write-ActionInfo 'Making image bootable...' + $bcdBootArgs = @( + "$($windowsDrive)\Windows", # Path to the \Windows on the VHD + "/s $systemDrive", # Specifies the volume letter of the drive to create the \BOOT folder on. + '/v' # Enabled verbose logging. + ) + $bcdBootArgs += '/f UEFI' # Specifies the firmware type of the target system partition + + Start-Executable -Executable $BCDBoot -Arguments $bcdBootArgs + + Write-ActionInfo 'Drive is bootable. Cleaning up...' + + # Remove system partition access path, if necessary + $systemPartition | Remove-PartitionAccessPath -AccessPath $systemPartition.AccessPaths[0] + } finally { + Write-ActionInfo "Dismounting $VHDFormat..." + Dismount-VHD -Path $VhdxPath + + #ejecting .iso - releasing drive letter. + Write-ActionInfo 'Dismounting .iso...' + Dismount-DiskImage -ImagePath $IsoPath + } +} + +$ErrorActionPreference = 'Stop' + +# download HCI VHDX or ISO +If (!(Test-Path -Path 'c:\ISOs')) { + log 'Creating c:\ISOs directory...' + mkdir c:\ISOs +} Else { + log 'ISOs directory already exists, skipping...' +} + +If ($hciVHDXDownloadURL) { + log 'Downloading HCI VHDX...' + If (! (Test-Path c:\ISOs\hci_os.vhdx)) { + [System.Net.WebClient]::new().DownloadFile($hciVHDXDownloadURL, 'c:\isos\hci_os.vhdx') + } Else { + log 'HCI VHDX already exists, skipping download...' + } +} ElseIf ($hciISODownloadURL) { + log 'Downloading HCI ISO...' + If (! (Test-Path c:\ISOs\hci_os.iso)) { + [System.Net.WebClient]::new().DownloadFile($hciISODownloadURL, 'c:\isos\hci_os.iso') + } Else { + log 'HCI ISO already exists, skipping download...' + } + + # convert ISO to VHDX + If (!(Test-Path 'c:\isos\hci_os.vhdx')) { + log 'Converting ISO to VHDX...' + New-VHDXFromISO -IsoPath 'c:\isos\hci_os.iso' -VhdxPath 'c:\isos\hci_os.vhdx' + } Else { + log 'HCI VHDX already exists, skipping conversion...' + + } +} Else { + log 'No download URL provided, cannot continue...' + Write-Error 'No download URL provided, cannot continue...' -ErrorAction Stop +} + +# create mount point directories on C:\ +log 'Creating mount points...' +For ($i = 0; $i -lt $hciNodeCount; $i++) { + $dirPath = "c:\diskmounts\hcinode$($i + 1)" + If (!(Test-Path -Path $dirPath)) { + mkdir $dirPath + } Else { + log "Mount point '$dirPath' already exists, skipping..." + } +} + +# format and mount disks +log 'Formatting and mounting disks...' +$count = 0 +$rawDisks = Get-Disk | Where-Object PartitionStyle -EQ 'RAW' +$rawDisks | + Initialize-Disk -PartitionStyle GPT -PassThru | + New-Partition -UseMaximumSize -AssignDriveLetter:$false | + Format-Volume -FileSystem NTFS | + Get-Partition | + Where-Object { $_.type -ne 'Reserved' } | + ForEach-Object { $count++; mountvol c:\diskMounts\HCINode$count $_.accesspaths[0] } + +log 'Copying VHDX to mount points...' +For ($i = 0; $i -lt $hciNodeCount; $i++) { + If (!(Test-Path -Path "c:\diskmounts\hcinode$($i + 1)\hci_os.vhdx")) { + Copy-Item -Path c:\isos\hci_os.vhdx -Destination "c:\diskmounts\hcinode$($i + 1)" + } Else { + log "HCI VHDX already exists at 'c:\diskmounts\hcinode$($i + 1)', skipping..." + } +} + +# install RRAS configure for routing +log 'Installing RRAS and configuring for routing...' +While (!(Test-Path -Path 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\RemoteAccess\RemoteAccess.psd1')) { + Start-Sleep -Seconds 5 + log 'Waiting for RRAS module to be available...' +} +Import-Module 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\RemoteAccess\RemoteAccess.psd1' +Install-RemoteAccess -VpnType RoutingOnly +Set-Service -Name RemoteAccess -StartupType Automatic -PassThru | Start-Service + +# install domain controller +log 'Checking whether AD is installed...' +If (!(Test-ADConnection)) { + log 'AD is not installed, installing...' + Import-Module 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ADDSDeployment\ADDSDeployment.psd1' + + $ADRecoveryPassword = ConvertTo-SecureString -Force -AsPlainText (New-Guid).guid + Install-ADDSForest -DomainName hci.local -DomainNetbiosName hci -ForestMode Default -DomainMode Default -InstallDns:$true -SafeModeAdministratorPassword $ADRecoveryPassword -NoRebootOnCompletion:$true -Force:$true +} Else { + log 'AD is already installed, skipping...' +} + +log 'Adding DNS forwarders...' +Import-Module 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DnsServer\DnsServer.psd1' +Add-DnsServerForwarder -IPAddress 8.8.8.8 + +# create reboot status file +If (Test-Path -Path 'C:\temp\Reboot2Completed.status') { + log 'Reboot has already been completed, skipping...' +} ElseIf (Test-Path -Path 'C:\temp\Reboot2Initiated.status') { + log 'Reboot has already been initiated, skipping...' +} Else { + log 'Reboot required, creating status file...' + Set-Content -Path 'C:\temp\Reboot2Required.status' -Value 'Reboot 2 Required' +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage4.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage4.ps1 new file mode 100644 index 0000000000..e6420c7d14 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage4.ps1 @@ -0,0 +1,41 @@ + +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-4.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage4] - $message" +} + +$ErrorActionPreference = 'Stop' + +# check for reboot status file, reboot if needed +If (Test-Path -Path 'C:\temp\Reboot2Required.status') { + log 'Reboot 2 is required' + + Remove-Item 'C:\temp\Reboot2Required.status' + Set-Content -Path 'C:\temp\Reboot2Initiated.status' -Value 'Reboot 2 Initiated' + + # use scheduled task to reboot the machine, ensuring the runCommand exits gracefully + $action = New-ScheduledTaskAction -Execute 'shutdown.exe' -Argument '-r -f -t 0' + $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(2) + $principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -LogonType ServiceAccount + $task = New-ScheduledTask -Action $action -Description 'Reboot 2' -Trigger $trigger -Principal $principal + Register-ScheduledTask -TaskName 'Reboot2' -InputObject $task + +} ElseIf (Test-Path -Path 'C:\temp\Reboot2Initiated.status') { + log 'Reboot 2 has been initiated and now completed' + + Remove-Item 'C:\temp\Reboot2Initiated.status' + Set-Content -Path 'C:\temp\Reboot2Completed.status' -Value 'Reboot 2 Completed' + +} ElseIf (Test-Path -Path 'C:\temp\Reboot2Completed.status') { + log 'Reboot 2 has been completed' + +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage5.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage5.ps1 new file mode 100644 index 0000000000..8f8b2ec203 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage5.ps1 @@ -0,0 +1,398 @@ +[CmdletBinding()] +param ( + [Parameter()] + [string] + $adminUsername, + + [Parameter()] + [string] + $adminPw, + + [Parameter()] + [int] + $hciNodeCount, + + [Parameter()] + [string] + $switchlessStorageConfig = 'switched' +) + +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-5.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage5] - $message" +} + +Function Test-ADConnection { + try { + If ((Get-Service -Name 'ADWS' -ErrorAction SilentlyContinue).Status -ne 'Running') { return $false } + $env:ADPS_LoadDefaultDrive = 0 + Import-Module -Name ActiveDirectory -ErrorAction Stop + [bool](Get-ADDomainController -Server $env:COMPUTERNAME -ErrorAction SilentlyContinue) + } catch { + $false + } +} + +$ErrorActionPreference = 'Stop' + +# create hyperv switches +log 'Creating Hyper-V switches...' +$existingSwitches = Get-VMSwitch + +If ($switchlessStorageConfig -eq 'switched') { + log 'Creating Hyper-V switches for switched storage configuration...' + If ($existingSwitches.Name -notcontains 'external' ) { New-VMSwitch -Name external -AllowManagementOS:$true -NetAdapterName Ethernet } + If ($existingSwitches.Name -notcontains 'hciNodeCompInternal' ) { New-VMSwitch -Name hciNodeCompInternal -SwitchType Internal -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeMgmtInternal' ) { New-VMSwitch -Name hciNodeMgmtInternal -SwitchType Internal -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeStoragePrivate' ) { New-VMSwitch -Name hciNodeStoragePrivate -SwitchType Private -EnableIov $true } +} ElseIf ($switchlessStorageConfig -eq 'switchless') { + If ($hciNodeCount -gt 3) { + log -message 'ERROR: Switchless storage configuration is only supported for 3 or fewer HCI nodes. Exiting script...' + Write-Error 'ERROR: Switchless storage configuration is only supported for 3 or fewer HCI nodes. Exiting script...' + exit 1 + } + + log 'Creating Hyper-V switches for switchless storage configuration...' + If ($existingSwitches.Name -notcontains 'external' ) { New-VMSwitch -Name external -AllowManagementOS:$true -NetAdapterName Ethernet } + If ($existingSwitches.Name -notcontains 'hciNodeCompInternal' ) { New-VMSwitch -Name hciNodeCompInternal -SwitchType Internal -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeMgmtInternal' ) { New-VMSwitch -Name hciNodeMgmtInternal -SwitchType Internal -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeStoragePrivateA' ) { New-VMSwitch -Name hciNodeStoragePrivateA -SwitchType Private -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeStoragePrivateB' ) { New-VMSwitch -Name hciNodeStoragePrivateB -SwitchType Private -EnableIov $true } + If ($existingSwitches.Name -notcontains 'hciNodeStoragePrivateC' ) { New-VMSwitch -Name hciNodeStoragePrivateC -SwitchType Private -EnableIov $true } +} + +# add IPs for host +log 'Adding IPs for host...' +$existingIPs = Get-NetIPAddress +If ($existingIPs.IPAddress -notcontains '172.20.0.1') { New-NetIPAddress -InterfaceAlias 'vEthernet (hciNodeMgmtInternal)' -IPAddress 172.20.0.1 -PrefixLength 24 } +If ($existingIPs.IPAddress -notcontains '10.20.0.1') { New-NetIPAddress -InterfaceAlias 'vEthernet (hciNodeCompInternal)' -IPAddress 10.20.0.1 -PrefixLength 24 } + +# configure NAT +log 'Restarting RemoteAccess service...' +Restart-Service RemoteAccess + +log 'Configuring NAT...' + +netsh routing ip nat uninstall +netsh routing ip nat install +netsh routing ip nat set global tcptimeoutmins=1440 udptimeoutmins=1 loglevel=ERROR +netsh routing ip nat add interface name="vEthernet (external)" mode=FULL +If (!$?) { + $message = "Failed to run netsh command: ''netsh routing ip nat add interface name='vEthernet (external)' mode=FULL''." + log $message + Write-Error $message +} +netsh routing ip nat add interface name="vEthernet (hciNodeCompInternal)" mode=PRIVATE +If (!$?) { + $message = "Failed to run netsh command: ''netsh routing ip nat add interface name='vEthernet (hciNodeCompInternal)' mode=PRIVATE''." + log $message + Write-Error $message +} +netsh routing ip nat add interface name="vEthernet (hciNodeMgmtInternal)" mode=PRIVATE +If (!$?) { + $message = "Failed to run netsh command: ''netsh routing ip nat add interface name='vEthernet (hciNodeMgmtInternal)' mode=PRIVATE''." + log $message + Write-Error $message +} + +# create DHCP scopes +log 'Creating DHCP scopes...' +$existingScopes = Get-DhcpServerv4Scope +If ($existingScopes.name -notcontains 'HCIComp') { Add-DhcpServerv4Scope -StartRange 10.20.0.10 -EndRange 10.20.0.250 -Name HCIComp -State Active -SubnetMask 255.255.255.0 } +If ($existingScopes.name -notcontains 'HCIMgmt') { Add-DhcpServerv4Scope -StartRange 172.20.0.10 -EndRange 172.20.0.250 -Name HCIMgmt -State Active -SubnetMask 255.255.255.0 } + +# test DC connectivity before attempting to authorize DHCP server in AD +log 'Testing DC connectivity...' +$count = 0 +While (!(Test-ADConnection) -and $count -lt 120) { + Start-Sleep -Seconds 5 + log 'Waiting for AD Web Services to be available...' + $count++ +} + +# authorize DHCP servers in AD for DNS updates +log 'Authorizing DHCP servers in AD for DNS updates...' +try { + $existingAuthorizedServers = Get-DhcpServerInDC -ErrorAction Stop +} catch { + log 'Failed to query authorized DHCP servers in AD. Waiting 120 seconds before retrying...' + Start-Sleep -Seconds 120 + $existingAuthorizedServers = Get-DhcpServerInDC +} + +If ($existingAuthorizedServers.IPAddress -notcontains '172.20.0.1') { Add-DhcpServerInDC -DnsName "$($env:COMPUTERNAME).hci.local" -IPAddress 172.20.0.1 } +If ($existingAuthorizedServers.IPAddress -notcontains '10.20.0.1') { Add-DhcpServerInDC -DnsName "$($env:COMPUTERNAME).hci.local" -IPAddress 10.20.0.1 } + +# set router and dns options for mgmt DHCP scope +log 'Setting router and dns options for mgmt DHCP scope...' +Set-DhcpServerv4OptionValue -ScopeId 172.20.0.0 -DnsDomain hci.local -DnsServer 172.20.0.1 -Router 172.20.0.1 + +# create HCI node VMs +log 'Creating HCI node VMs...' +$existingVMs = Get-VM +For ($i = 1; $i -le $hciNodeCount; $i++) { + $hciNodeName = "hcinode$i" + $hciNodePath = "C:\diskMounts\$hciNodeName" + + If ($existingVMs.name -notcontains $hciNodeName) { New-VM -Name $hciNodeName -MemoryStartupBytes 32GB -BootDevice VHD -SwitchName hciNodeMgmtInternal -Path C:\diskMounts\ -VHDPath "$hciNodePath\hci_os.vhdx" -Generation 2 } +} + +# configure HCI node VMs +log 'Configuring HCI node VMs...' +log 'Setting VM processor count to 16 and enabling virtualization extensions...' +Get-VM | Set-VMProcessor -ExposeVirtualizationExtensions $true -Count 8 + +log 'Setting VM key protector and enabling TPM...' +Get-VM | ForEach-Object { + If (($_ | Get-VMKeyProtector).Length -eq 4) { + log "Adding key protector for VM '$($_.Name)'" + $_ | Set-VMKeyProtector -NewLocalKeyProtector + } Else { + log "Key protector already exists for VM '$($_.Name)'" + } + + If (($_ | Get-VMSecurity).TpmEnabled -eq $false) { + log "Enabling TPM for VM '$($_.Name)'" + $_ | Enable-VMTPM + } Else { + log "TPM already enabled for VM '$($_.Name)'" + } +} + + +# rename nic to mgmt +log 'Renaming first network adapter on HCI nodes...' +if (Get-VMNetworkAdapter -Name 'Network Adapter' -VMName * -ErrorAction SilentlyContinue) { Rename-VMNetworkAdapter -NewName mgmt -VMName * -Name 'Network Adapter' } +Get-VM | Get-VMNetworkAdapter -Name 'mgmt' | Set-VMNetworkAdapter -DeviceNaming On + +# add additional NICs to HCI node VMs +log 'Adding additional NICs to HCI node VMs...' +ForEach ($existingVM in (Get-VM)) { + $existingNICs = Get-VMNetworkAdapter -VM $existingVM + If ($existingNICs.name -notcontains 'comp0') { $existingVM | Add-VMNetworkAdapter -Name comp0 -SwitchName hciNodeCompInternal -DeviceNaming On } + If ($existingNICs.name -notcontains 'comp1') { $existingVM | Add-VMNetworkAdapter -Name comp1 -SwitchName hciNodeCompInternal -DeviceNaming On } + + If ($switchlessStorageConfig -eq 'switched') { + log "Adding NICs to VM '$($existingVM.name)' for switched storage configuration..." + If ($existingNICs.name -notcontains 'smb0') { $existingVM | Add-VMNetworkAdapter -Name smb0 -SwitchName hciNodeStoragePrivate -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + If ($existingNICs.name -notcontains 'smb1') { $existingVM | Add-VMNetworkAdapter -Name smb1 -SwitchName hciNodeStoragePrivate -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '712' -NativeVlanId 0 } + } ElseIf ($switchlessStorageConfig -eq 'switchless') { + log "Adding NICs to VM '$($existingVM.name)' for switchless storage configuration..." + + switch ($existingVM.Name[-1]) { + 1 { + If ($existingNICs.name -notcontains 'smb0') { $existingVM | Add-VMNetworkAdapter -Name smb0 -SwitchName hciNodeStoragePrivateA -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + If ($existingNICs.name -notcontains 'smb1') { $existingVM | Add-VMNetworkAdapter -Name smb1 -SwitchName hciNodeStoragePrivateB -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + } + 2 { + If ($existingNICs.name -notcontains 'smb0') { $existingVM | Add-VMNetworkAdapter -Name smb0 -SwitchName hciNodeStoragePrivateA -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + If ($existingNICs.name -notcontains 'smb1') { $existingVM | Add-VMNetworkAdapter -Name smb1 -SwitchName hciNodeStoragePrivateC -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + } + 3 { + If ($existingNICs.name -notcontains 'smb0') { $existingVM | Add-VMNetworkAdapter -Name smb0 -SwitchName hciNodeStoragePrivateB -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + If ($existingNICs.name -notcontains 'smb1') { $existingVM | Add-VMNetworkAdapter -Name smb1 -SwitchName hciNodeStoragePrivateC -DeviceNaming On -Passthru | Set-VMNetworkAdapterVlan -Trunk -AllowedVlanIdList '711' -NativeVlanId 0 } + } + Default {} + } + } +} + +# add disks to HCI node VMs +log 'Adding disks to HCI node VMs...' +Foreach ($vm in (Get-VM)) { + (1..4) | ForEach-Object { + $diskPath = "C:\diskMounts\$($vm.Name)\hciNodeDisk$($_).vhdx" + If (!(Test-Path -Path $diskPath)) { + log "Creating disk: $diskPath" + New-VHD -Path $diskPath -SizeBytes 1TB -Dynamic | Out-Null + } + If ($VM.HardDrives.Path -notcontains $diskPath) { + log "Adding disk: $diskPath to VM: $($vm.Name)" + Add-VMHardDiskDrive -VMName $vm.Name -ControllerType SCSI -ControllerNumber 0 -ControllerLocation $_ -Path $diskPath | Out-Null + } + } +} + +# enable mac soofing on HCI node VMs +log 'Enabling MAC spoofing on HCI node VMs...' +Get-VM | Get-VMNetworkAdapter | Set-VMNetworkAdapter -MacAddressSpoofing On + +# define unattend.xml for HCI node VMs template +$unattendSource = @' + + + + + $hciNodeName + Organization + Owner + UTC + + + false + + + 1 + + + false + + + + + + true + + + + + + + + + true + true + true + Work + 1 + + + + powershell.exe -command "Get-NetAdapterAdvancedProperty -DisplayName 'Hyper-V Network Adapter Name' | Foreach-Object {`$_ | Get-NetAdapter | Rename-NetAdapter -NewName `$_.DisplayValue}" + 1 + false + + + powershell.exe -command "Enable-WindowsOptionalFeature -Online -FeatureName 'microsoft-hyper-v-online' -all -NoRestart" + 2 + false + + + powershell.exe -command "Remove-Item -Path 'C:\unattend.xml' -Force" + 3 + false + + + shutdown -r -f -t 0 + 4 + false + + + + Administrator + true + 1 + + $adminPw +

true</PlainText> + </Password> + </AutoLogon> + <UserAccounts> + <AdministratorPassword> + <Value>$adminPw</Value> + <PlainText>True</PlainText> + </AdministratorPassword> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Password> + <Value>$adminPw</Value> + <PlainText>true</PlainText> + </Password> + <Description>HCI Admin User</Description> + <DisplayName>$adminUsername</DisplayName> + <Group>Administrators;Power Users</Group> + <Name>$adminUsername</Name> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + </component> + <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <InputLocale>en-us</InputLocale> + <SystemLocale>en-us</SystemLocale> + <UILanguage>en-us</UILanguage> + <UILanguageFallback>en-us</UILanguageFallback> + <UserLocale>en-us</UserLocale> + </component> + </settings> +</unattend> +'@ + +# inject updated sysp answer file into each HCI node disk +log 'Injecting updated sysprep answer file into each HCI node disk...' +For ($i = 1; $i -le $hciNodeCount; $i++) { + $hciNodeName = "hciNode$i" + $hciProductKey = '' + + Push-Location c:\diskMounts\$hciNodeName + + If (!(Test-Path -Path unattend_injected.status) -and (Get-VM -Name $hciNodeName).State -eq 'Off') { + log "Injecting unattend.xml into HCI node disk '$hciNodeName'..." + $mountedVolume = Mount-VHD .\hci_os.vhdx -Passthru | Get-Disk | Get-Partition | Get-Volume | Where-Object FileSystemType -EQ 'NTFS' + + $clone = $unattendSource.psobject.copy() + $clone = $ExecutionContext.InvokeCommand.ExpandString($clone) + + Set-Content -Path "$($mountedVolume.DriveLetter):\unattend.xml" -Value $clone -Force + + Dismount-VHD .\hci_os.vhdx + + Set-Content 'unattend_injected.status' -Value 'Unattend.xml injected' + } Else { + log "Unattend.xml already injected into HCI node disk '$hciNodeName'." + } + + Pop-Location +} + +# start HCI node VMs +If (Get-VM | Where-Object State -EQ 'Off') { + log 'Starting HCI node VMs...' + try { + $errorActionPreference = 'Stop' + Get-VM | Start-VM + } catch { + log "Failed to start HCI node VMs. $_" + Write-Error "Failed to start HCI node VMs. $_" + } + + #wait for vms to boot + log 'Waiting 300s for VMs to boot and apply sysprep...' + Start-Sleep -Seconds 300 +} Else { + log 'HCI node VMs are already running.' +} + +# create DHCP reservations for HCI node VMs management interfaces +log 'Checking DHCP reservations for HCI node VMs management interfaces...' +$existingReservations = Get-DhcpServerv4Reservation -ScopeId 172.20.0.0 +For ($i = 1; $i -le $hciNodeCount; $i++) { + $hciNodeName = "hciNode$i" + $hciNodeIP = "172.20.0.$(9 + $i)" + If ($existingReservations.Description -notcontains $hciNodeName) { + log "Creating DHCP reservation for HCI node '$hciNodeName' with IP '$hciNodeIP'..." + + $mgmtNIC = Get-VMNetworkAdapter -VMName $hciNodeName -Name mgmt + + If ($mgmtNIC) { + $mgmtMac = $mgmtNIC.MacAddress -split '(.{2})' -ne '' -join '-' + + log "Creating DHCP reservation for HCI node '$hciNodeName' with IP '$hciNodeIP' for MAC address '$mgmtMac'..." + Add-DhcpServerv4Reservation -ScopeId 172.20.0.0 -Name $hciNodeName -IPAddress $hciNodeIP -ClientId $mgmtMac -Description $hciNodeName + } Else { + log "Failed to create DHCP reservation for HCI node '$hciNodeName'. Could not find NIC named 'mgmt'." + Write-Error "Failed to create DHCP reservation for HCI node '$hciNodeName'. Could not find NIC named 'mgmt'." + exit 1 + } + } Else { + log "DHCP reservation for HCI node '$hciNodeName' already exists." + } +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage6.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage6.ps1 new file mode 100644 index 0000000000..454e5ad45b --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage6.ps1 @@ -0,0 +1,354 @@ +[CmdletBinding()] +param ( + [Parameter()] + [String] + $resourceGroupName, + + [Parameter()] + [String] + $subscriptionId, + + [Parameter()] + [String] + $tenantId, + + [Parameter()] + [String] + $location, + + [Parameter()] + [String] + $accountName, + + [Parameter()] + [String] + $adminUsername, + + [Parameter()] + [String] + $adminPw, + + [Parameter()] + [String] + $arcGatewayId, + + [Parameter()] + [String] + $domainOUPath = 'OU=HCI,DC=HCI,DC=local', + + [Parameter()] + [string] + $deploymentUsername, + + [Parameter()] + [string] + $proxyServerEndpoint, #http://[Proxy_Server_Address]:[Proxy_Port], + + [parameter()] + [string] + $proxyBypassString, #"localhost;127.0.0.1;*.contoso.com;node1;node2;192.168.1.*;s-cluster", + + [Parameter()] + [string] + $userAssignedManagedIdentityClientId +) + +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-6.log' + ) + + If (!(Test-Path -Path 'C:\temp')) { + New-Item -Path 'C:\temp' -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage6] - $message" +} + +$ErrorActionPreference = 'Stop' + +# export or re-import local administrator credential +# we do this to support re-run of the template. If deployed, the HCI node password will be set to the password provided in the template, but future re-runs will generate a new password. +If (!(Test-Path -Path 'C:\temp\hciHostDeployAdminCred.xml')) { + log "Exporting local '$($adminUsername)' credential (for re-use if script is re-run)..." + $adminCred = [pscredential]::new($adminUsername, (ConvertTo-SecureString -AsPlainText -Force $adminPw)) + $adminCred | Export-Clixml -Path 'C:\temp\hciHostDeployAdminCred.xml' +} Else { + log "Re-importing local '$($adminUsername)' credential..." + $adminCredOld = Import-Clixml -Path 'C:\temp\hciHostDeployAdminCred.xml' + + $newCredFileName = 'hciHostDeployAdminCred_{0}.xml' -f (Get-Date -Format 'yyyyMMddHHmmss') + log "Renaming the old credential file to '$newCredFileName' prevent overwriting..." + Rename-Item -Path 'C:\temp\hciHostDeployAdminCred.xml' -NewName $newCredFileName + + log "Exporting local '$($adminUsername)' credential (for re-use if script is re-run)..." + $adminCred = [pscredential]::new($adminUsername, (ConvertTo-SecureString -AsPlainText -Force $adminPw)) + $adminCred | Export-Clixml -Path 'C:\temp\hciHostDeployAdminCred.xml' +} + +If (!(Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue)) { Register-PSRepository -Default } +If (!(Get-PackageProvider -Name Nuget -ListAvailable -ErrorAction SilentlyContinue)) { Install-PackageProvider -Name NuGet -Confirm:$false -Force } +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + +Install-Module Az + +# get an access token for the VM MSI, which has been granted rights and will be used for the HCI Arc Initialization +log "Logging in to Azure with user-assigned managed identity '$($userAssignedManagedIdentityClientId)'..." +Login-AzAccount -Identity -Subscription $subscriptionId -AccountId $userAssignedManagedIdentityClientId + +log 'Getting access token for Azure Stack HCI Arc initialization...' +$t = Get-AzAccessToken -ResourceUrl 'https://management.azure.com' | Select-Object -ExpandProperty Token + +# pre-create AD objects +log 'Pre-creating AD objects with deployment username '$deploymentUsername'...' +$deployUserCred = [pscredential]::new($deploymentUsername, (ConvertTo-SecureString -AsPlainText -Force $adminPw)) + +Install-Module AsHciADArtifactsPreCreationTool +New-HciAdObjectsPreCreation -AzureStackLCMUserCredential $deployUserCred -AsHciOUName $domainOUPath + +## set the LCM deployUser password to the adminPw value - this aligns the password with the KeyVault during re-runs +log "Setting deploy user '$deploymentUsername's password..." +Set-AdAccountPassword -Identity $deploymentUsername -NewPassword (ConvertTo-SecureString -AsPlainText -Force $adminPw) -Reset -Confirm:$false + +# initialize arc on hci nodes +log 'Initializing Azure Arc on HCI nodes...' + +# wait for VMs to reach 'Running' state +log 'Checking that VMs are running...' +$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() +while ((Get-VM | Where-Object State -NE 'Running') -and $stopwatch.Elapsed.TotalMinutes -lt 15) { + log "Waiting for HCI node VMs to reach 'Running' state. Current state: $((Get-VM) | Select-Object Name,State)..." + Start-Sleep -Seconds 30 +} + +If ($stopwatch.Elapsed.TotalMinutes -ge 15) { + log "HCI node VMs did not reach 'Running' state within 15 minutes. Exiting..." + Write-Error "HCI node VMs did not reach 'Running' state within 15 minutes. Exiting..." + Exit 1 +} + +log "Creating PSSessions to HCI nodes [$((Get-VM).Name -join ',')]..." +try { + $VMs = Get-VM + If ($adminCredOld) { + log 'Using old local administrator credential from exported CliXML...' + $localAdminCred = $adminCredOld + } Else { + log 'Using new local administrator credential from parameter input...' + $localAdminCred = $adminCred + } + $sessions = New-PSSession -VMName $VMs.Name -Credential $localAdminCred -ErrorAction Stop + + log "Created '$(($sessions | Where-Object State -EQ 'Opened').Count)' PSSessions to HCI nodes [$($VMs.Name -join ',')]." +} catch { + If ($_ -like '*The credential is invalid*') { + log 'Failed to create PSSessions with "The credential is invalid" error. This is likely due to the password not matching the local administrator password on the HCI nodes. Retrying with the older passwords...' + + $credFiles = Get-ChildItem -Path 'C:\temp\*' -Include 'hciHostDeployAdminCred*.xml' -Exclude 'hciHostDeployAdminCred.xml' + + If ($credFiles.count -eq 0) { + log 'No old credential files found. Exiting...' + Write-Error 'No old credential files found. Exiting...' + Exit 1 + } + + :retryCreds ForEach ($credFile in $credFiles) { + log "Attempting login with credential file '$($credFile.name)'..." + $localAdminCred = Import-Clixml -Path $credFile.FullName + + try { + $sessions = New-PSSession -VMName $VMs.Name -Credential $localAdminCred -ErrorAction Stop + + If (($sessions | Where-Object State -EQ 'Opened').count -eq $VMs.Count) { + log "Created '$(($sessions | Where-Object State -EQ 'Opened').Count)' PSSessions to HCI nodes [$($VMs.Name -join ',')]." + break retryCreds + } + } catch { + log "Failed to create PSSessions with credential file '$($credFile.name)'. Error: $_" + continue + } finally { + If (($sessions | Where-Object State -EQ 'Opened').count -eq $VMs.Count) { + log "Created '$(($sessions | Where-Object State -EQ 'Opened').Count)' PSSessions to HCI nodes [$($VMs.Name -join ',')]." + $adminCredOld = $localAdminCred + } + } + + } + } else { + log "Failed to create PSSessions to HCI nodes [$($VMs.Name -join ',')]. $sessions $_ Exiting..." + Write-Error "Failed to create PSSessions to HCI nodes [$($VMs.Name -join ',')]. $sessions $_ Exiting..." + Exit 1 + } +} + +# update local admin password to match the adminPw value +If ($adminCredOld) { + log 'Updating local administrator password to match the adminPw value...' + Invoke-Command -VMName (Get-VM).Name -Credential $adminCredOld { + $ErrorActionPreference = 'Stop' + + $adminPw = $args[0] + $adminUsername = $args[1] + + Write-Host "$($env:computerName):Setting local administrator password to match the adminPw value..." + $adminCred = [pscredential]::new($adminUsername, (ConvertTo-SecureString -AsPlainText -Force $adminPw)) + Set-LocalUser -Name $adminUsername -Password $adminCred.Password -Confirm:$false + } -ArgumentList $adminPw, $adminUsername +} Else { + log "Password for '$($adminUsername)' should already match the adminPw value..." +} + +# disable IPv6 on all HCI nodes +log 'Disabling IPv6 on HCI nodes...' +Invoke-Command -VMName (Get-VM).Name -Credential $adminCred { + reg add hklm\system\currentcontrolset\services\tcpip6\parameters /v DisabledComponents /t REG_DWORD /d 0xFF /f +} + +# set proxy settings if provided +if (![string]::IsNullOrEmpty($proxyServerEndpoint) -and ![string]::IsNullOrEmpty($proxyBypassString)) { + log 'Both -proxyServerEndpoiint and -proxyBypassString passed, setting proxy settings...' + log "Proxy Server Endpoint: $proxyServerEndpoint" + log "Proxy Bypass String: $proxyBypassString" + + If ($proxyBypassString -eq 'GENERATE_PROXY_BYPASS_DYNAMICALLY') { + log 'Generating proxy bypass string dynamically...' + $proxyBypassString = '127.0.0.1;localhost;172.20.0.*;*.hci.local;hcicluster;172.20.0.2;172.20.0.3;172.20.0.4;172.20.0.5;*.svc' + For ($i = 1; $i -le (Get-VM).count; $i++) { + $proxyBypassString += ";hcinode$i" + $proxyBypassString += ';172.20.0.{0}' -f (9 + $i) + } + } + + log 'Configuring proxy settings on HCI nodes...' + $proxyConfigLogs = Invoke-Command -VMName (Get-VM).Name -Credential $adminCred { + $ErrorActionPreference = 'Stop' + + $proxyServerEndpoint = $args[0] + $proxyBypassString = $args[1] + + ## install winInetProxy module + If (!(Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) { Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force } + If (!(Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue)) { Register-PSRepository -Default } + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + If (!(Get-InstalledModule -Name WinInetProxy -ErrorAction SilentlyContinue)) { Install-Module WinInetProxy -Force } + Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted + + ## set WinInet proxy settings + Set-WinInetProxy -ProxyServer $proxyServerEndpoint -ProxyBypass $proxyBypassString -ProxySettingsPerUser 0 + + ## set winhttp proxy settings + Set-WinhttpProxy -ProxyServer $proxyServerEndpoint -BypassList $proxyBypassString + + ## set proxy environment variables + $proxyBypassStringEnv = $proxyBypassString.replace(';', ',').replace('*', '').replace('172.20.0.*', '172.20.0.0/24') + $proxyBypassStringEnv += ',.svc' + + [Environment]::SetEnvironmentVariable('HTTPS_PROXY', $proxyServerEndpoint, 'Machine') + [Environment]::SetEnvironmentVariable('HTTP_PROXY', $proxyServerEndpoint, 'Machine') + [Environment]::SetEnvironmentVariable('NO_PROXY', $proxyBypassStringEnv, 'Machine') + + Write-Output "[$($env:COMPUTERNAME)] WinInetProxy Settings: $(Get-WinhttpProxy -Advanced)" + Write-Output "[$($env:COMPUTERNAME)] WinhttpProxy Settings: $(Get-WinhttpProxy -Default)" + + Write-Output ("[{0}] Environment Variables {1}: '{2}'" -f $env:COMPUTERNAME, 'HTTPS_PROXY', [Environment]::GetEnvironmentVariable('HTTPS_PROXY', 'Machine')) + Write-Output ("[{0}] Environment Variables {1}: '{2}'" -f $env:COMPUTERNAME, 'HTTP_PROXY', [Environment]::GetEnvironmentVariable('HTTP_PROXY', 'Machine')) + Write-Output ("[{0}] Environment Variables {1}: '{2}'" -f $env:COMPUTERNAME, 'NO_PROXY', [Environment]::GetEnvironmentVariable('NO_PROXY', 'Machine')) + + } -ArgumentList $proxyServerEndpoint, $proxyBypassString + + $proxyConfigLogs | ForEach-Object { + log $_ + } +} Else { + log "Skipping proxy settings because both -proxyServerEndpoint and -proxyBypassString were not passed... (proxyServerEndpoint: '$proxyServerEndpoint', proxyBypassString:'$proxyBypassString')" +} + +## test node internet connection - required for Azure Arc initialization +$firstVM = Get-VM | Select-Object -First 1 +log "Testing node internet connection on VM '$($firstVM.Name)'..." +$testNodeInternetConnection = Invoke-Command -VMName $firstVM.Name -Credential $adminCred { + [bool](Invoke-RestMethod ipinfo.io -UseBasicParsing) +} + +If (!$testNodeInternetConnection) { + log "Node '$($firstVM.name)' does not have internet connection. Check RRAS NAT configuration. Exiting..." + Write-Error "Node '$($firstVM.name)' does not have internet connection. Check RRAS NAT configuration. Exiting..." + Exit 1 +} Else { + log "Node '$($firstVM.name)' has internet connection. Curl IPInfo: '$($testNodeInternetConnection)'" +} + +## create jobs for each node to initialize Azure Arc +log "Creating Azure Arc initialization jobs for HCI nodes [$((Get-VM).Name -join ',')]. ArcGatewayId: '$arcGatewayId', ProxyServerEndpoint: '$proxyServerEndpoint'..." +$arcInitializationJobs = Invoke-Command -VMName (Get-VM).Name -Credential $adminCred { + $ErrorActionPreference = 'Stop' + + $t = $args[0] + $subscriptionId = $args[1] + $resourceGroupName = $args[2] + $tenantId = $args[3] + $location = $args[4] + $accountName = $args[5] + $arcGatewayId = $args[6] + $proxyServerEndpoint = $args[7] + $proxyBypassString = $args[8] + + $optionalParameters = @{} + + If ($arcGatewayId) { + $optionalParameters += @{ + 'arcGatewayId' = $arcGatewayId + } + } + If ($proxyServerEndpoint) { + $optionalParameters += @{ + 'proxy' = $proxyServerEndpoint + 'proxyBypass' = $proxyBypassString + } + } + + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null + If (!(Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue)) { Register-PSRepository -Default } + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + Install-Module Az.Resources + Install-Module -Name AzsHCI.ARCinstaller # -RequiredVersion '0.2.2690.99' # hardcode for 2408 testing + Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted + + #wait for bootstrap service to be reachable + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + While (!(Test-NetConnection -ComputerName '127.0.0.1' -Port 9098 -InformationLevel Quiet) -and $stopwatch.Elapsed.TotalMinutes -lt 30) { + Write-Host 'Waiting for bootstrap service at 127.0.0.1:9098 to be reachable...' + Start-Sleep -Seconds 30 + } + If ($stopwatch.Elapsed.TotalMinutes -ge 30) { + Write-Error 'Bootstrap service at 127.0.0.1:9098 did not become reachable within 30 minutes. Exiting...' -ErrorAction Stop + } + + try { + Invoke-AzStackHciArcInitialization -SubscriptionID $subscriptionId -ResourceGroup $resourceGroupName -TenantID $tenantId -Cloud AzureCloud -AccountID $accountName -ArmAccessToken $t -Region $location -ErrorAction Stop @optionalParameters + } catch { + Write-Error $_ -ErrorAction Stop + } +} -AsJob -ArgumentList $t, $subscriptionId, $resourceGroupName, $tenantId, $location, $accountName, $arcGatewayId, $proxyServerEndpoint, $proxyBypassString + +log 'Waiting up to 30 minutes for Azure Arc initialization to complete on nodes...' + +$arcInitializationJobs | Wait-Job -Timeout 1800 + +# check for failed arc initialization jobs +log 'Checking status of Azure Arc initialization jobs...' +$arcInitializationJobs | ForEach-Object { + $job = $_ + log "[$($job.ComputerName)] Job output (Receive-Job): '$($job | Receive-Job -Keep -ErrorAction Continue | Out-String)'" + Get-Job -Id $job.Id -IncludeChildJob | Receive-Job -ErrorAction SilentlyContinue | ForEach-Object { + If ($_.Exception -or $_.state -eq 'Failed') { + log "Azure Arc initialization failed on node '$($job.Location)' with error: $($_.Exception.Message)" + Exit 1 + } Else { + log "[$($job.ComputerName)] Job output: '$($_ | ConvertTo-Json -Compress)'" + + } + } +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage7.ps1 b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage7.ps1 new file mode 100644 index 0000000000..77c0193f7b --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/hciHostStage7.ps1 @@ -0,0 +1,207 @@ +param( + [Parameter()] + [String] + $resourceGroupName, + + [Parameter()] + [String] + $subscriptionId, + + [Parameter()] + [int] + $hciNodeCount, + + [Parameter()] + [String] + $userAssignedManagedIdentityClientId + +) +Function log { + Param ( + [string]$message, + [string]$logPath = 'C:\temp\hciHostDeploy-7.log' + ) + + If (!(Test-Path -Path C:\temp)) { + New-Item -Path C:\temp -ItemType Directory + } + + Write-Host $message + Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [hciHostStage7] - $message" +} + +$ErrorActionPreference = 'Stop' + +$hciNodeNames = @() +for ($i = 1; $i -le $hciNodeCount; $i++) { + $hciNodeNames += "hcinode$i" +} + +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted +Install-Module -Name Az.ConnectedMachine -Force -AllowClobber -Scope CurrentUser -Repository PSGallery -ErrorAction SilentlyContinue +Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted + +log "Logging in to Azure with user-assigned managed identity '$($userAssignedManagedIdentityClientId)'..." +Login-AzAccount -Identity -Subscription $subscriptionId -AccountId $userAssignedManagedIdentityClientId + +log "Waiting for HCI Arc Machines to exist in the resource group '$($resourceGroupName)'..." + +$timer = [System.Diagnostics.Stopwatch]::StartNew() +While (($arcMachines = Get-AzConnectedMachine -ResourceGroupName $resourceGroupName | Where-Object { $_.name -in ($hciNodeNames) }).Count -lt $hciNodeNames.Count -and $timer.Elapsed.TotalMinutes -lt 60) { + log "Found '$($arcMachines.Count)' HCI Arc Machines, waiting for '$($hciNodeNames.Count)' machines for up to 1 hour..." + Start-Sleep -Seconds 30 +} +If ($timer.Elapsed.TotalMinutes -gt 60) { + log 'HCI Arc Machines did not exist within the 1 hour timeout period' + Write-Error 'HCI Arc Machines did not exist within the 1 hour timeout period' -ErrorAction Stop + Exit 1 +} Else { + log "All HCI Arc Machines exist in the resource group '$($resourceGroupName)'" +} + +log 'Waiting up to two hours for HCI Arc Machine extensions to be installed...' +$timer = [System.Diagnostics.Stopwatch]::StartNew() +$allExtensionsReady = $false +while (!$allExtensionsReady -and $timer.Elapsed.TotalMinutes -lt 120) { + $allExtensionsReadyCheck = $true + foreach ($arcMachine in $arcMachines) { + $extensions = Get-AzConnectedMachineExtension -ResourceGroupName $resourceGroupName -MachineName $arcMachine.Name + if ($extensions.MachineExtensionType -notcontains 'TelemetryAndDiagnostics' -or $extensions.MachineExtensionType -notcontains 'DeviceManagementExtension' -or $extensions.MachineExtensionType -notcontains 'LcmController' -or $extensions.MachineExtensionType -notcontains 'EdgeRemoteSupport') { + log "Waiting for extensions to be installed on HCI Arc Machine '$($arcMachine.Name)'..." + + # install extensions if not already installed + log "Installing any missing extensions on HCI Arc Machine '$($arcMachine.Name)'..." + $extensionParams = @{ + ResourceGroupName = $resourceGroupName + MachineName = $arcMachine.Name + Location = $arcMachine.Location + ErrorAction = 'Continue' + } + + # Invoke-AzStackHciArcInitialization seemingly misses installing some extensions some of the time - so we'll install them here if missing + If ($extensions.MachineExtensionType -notcontains 'TelemetryAndDiagnostics') { + log "Installing TelemetryAndDiagnostics extension on HCI Arc Machine '$($arcMachine.Name)'..." + New-AzConnectedMachineExtension -Name 'AzureEdgeTelemetryAndDiagnostics' -Publisher 'Microsoft.AzureStack.Observability' -ExtensionType 'TelemetryAndDiagnostics' -NoWait @extensionParams + } + If ($extensions.MachineExtensionType -notcontains 'DeviceManagementExtension') { + log "Installing DeviceManagementExtension extension on HCI Arc Machine '$($arcMachine.Name)'..." + New-AzConnectedMachineExtension -Name 'AzureEdgeDeviceManagement' -Publisher 'Microsoft.Edge' -ExtensionType 'DeviceManagementExtension' -NoWait @extensionParams + } + If ($extensions.MachineExtensionType -notcontains 'LcmController') { + log "Installing LcmController extension on HCI Arc Machine '$($arcMachine.Name)'..." + New-AzConnectedMachineExtension -Name 'AzureEdgeLifecycleManager' -Publisher 'Microsoft.AzureStack.Orchestration' -ExtensionType 'LcmController' -NoWait @extensionParams + } + If ($extensions.MachineExtensionType -notcontains 'EdgeRemoteSupport') { + log "Installing EdgeRemoteSupport extension on HCI Arc Machine '$($arcMachine.Name)'..." + New-AzConnectedMachineExtension -Name 'AzureEdgeRemoteSupport' -Publisher 'Microsoft.AzureStack.Observability' -ExtensionType 'EdgeRemoteSupport' -NoWait @extensionParams + } + + $allExtensionsReadyCheck = $false + continue + } elseIf (($extensionState = $extensions | Where-Object MachineExtensionType -EQ 'TelemetryAndDiagnostics').ProvisioningState -ne 'Succeeded') { + log "Waiting for TelemetryAndDiagnostics extension to be installed on HCI Arc Machine '$($arcMachine.Name)'. Current state: '$($extensionState.ProvisioningState)'..." + $allExtensionsReadyCheck = $false + } elseIf (($extensionState = $extensions | Where-Object MachineExtensionType -EQ 'DeviceManagementExtension').ProvisioningState -ne 'Succeeded') { + log "Waiting for DeviceManagementExtension extension to be installed on HCI Arc Machine '$($arcMachine.Name)'. Current state: '$($extensionState.ProvisioningState)'..." + $allExtensionsReadyCheck = $false + } elseIf (($extensionState = $extensions | Where-Object MachineExtensionType -EQ 'LcmController').ProvisioningState -ne 'Succeeded') { + log "Waiting for LcmController extension to be installed on HCI Arc Machine '$($arcMachine.Name)'. Current state: '$($extensionState.ProvisioningState)'..." + $allExtensionsReadyCheck = $false + } elseIf (($extensionState = $extensions | Where-Object MachineExtensionType -EQ 'EdgeRemoteSupport').ProvisioningState -ne 'Succeeded') { + log "Waiting for EdgeRemoteSupport extension to be installed on HCI Arc Machine '$($arcMachine.Name)'. Current state: '$($extensionState.ProvisioningState)'..." + $allExtensionsReadyCheck = $false + } else { + log "All extensions are installed and ready on HCI Arc Machine '$($arcMachine.Name)'" + } + } + $allExtensionsReady = $allExtensionsReadyCheck + If (!$allExtensionsReady) { + log 'waiting 30 seconds to check extensions again...' + Start-Sleep -Seconds 30 + } +} + +If (!$allExtensionsReady) { + log 'Extensions did not install within the two hour timeout period' + Exit 1 +} Else { + log 'All extensions are installed and ready on all HCI Arc Machines' +} + +# import local administrator credential (exported in stage 6) +log "Re-importing local '$($adminUsername)' credential..." +$adminCred = Import-Clixml -Path 'C:\temp\hciHostDeployAdminCred.xml' + +# name net adapters - seems to be required on 2405 +log 'Renaming network adapters on HCI nodes...' +$vmNicLocalNamingOut = Invoke-Command -VMName (Get-VM).Name -Credential $adminCred { + $ErrorActionPreference = 'Stop' + + Get-NetAdapter | ForEach-Object { + $adapter = $_ + + try { + Write-Output "Getting Hyper-V network adapter name for '$($adapter.Name)' on VM '$($env:COMPUTERNAME)'..." + $newAdapterName = Get-NetAdapterAdvancedProperty -RegistryKeyword HyperVNetworkAdapterName -Name $adapter.Name | Select-Object -ExpandProperty DisplayValue + } catch { + Write-Output "Failed to get Hyper-V network adapter name for '$($adapter.Name)' on VM '$($env:COMPUTERNAME)'. Ensure DeviceNaming is turned on for the VM Network Adapter! $_ Exiting..." + Write-Error "Failed to get Hyper-V network adapter name for '$($adapter.Name)' on VM '$($env:COMPUTERNAME)'. Ensure DeviceNaming is turned on for the VM Network Adapter! $_ Exiting..." -ErrorAction Stop + Exit 1 + } + + If ($adapter.InterfaceAlias -ne $newAdapterName) { + Write-Output "Renaming network adapter '$($adapter.InterfaceAlias)' to '$newAdapterName' on VM '$($env:COMPUTERNAME)'..." + Rename-NetAdapter -Name $adapter.Name -NewName $newAdapterName + } Else { + Write-Output "Network adapter '$($adapter.InterfaceAlias)' is already named correctly on VM '$($env:COMPUTERNAME)'..." + } + } +} +log "VM NIC local naming output: $vmNicLocalNamingOut" + +# change dynamically assigned mgmt IP addresses to static IPs as required by validation +log 'Changing dynamically assigned mgmt IP addresses to static IPs on HCI nodes...' +$ipChangeOutput = Invoke-Command -VMName (Get-VM).Name -Credential $adminCred { + $ErrorActionPreference = 'Stop' + + $dhcpIpConfig = Get-NetIPConfiguration -InterfaceAlias 'mgmt' + $prefixLength = Get-NetIPAddress -InterfaceAlias 'mgmt' -AddressFamily IPv4 | Select-Object -ExpandProperty PrefixLength + $dnsClientConfig = Get-DnsClientServerAddress -InterfaceAlias 'mgmt' -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses + + try { + If (!(Get-NetIPInterface -InterfaceAlias 'mgmt' -Dhcp Enabled -ErrorAction SilentlyContinue)) { + Write-Output "[$env:computerName]DHCP is already disabled on network interface 'mgmt'..." + } Else { + Write-Output "[$env:computerName]Disabling DHCP on network interface 'mgmt'..." + Set-NetIPInterface -InterfaceAlias 'mgmt' -Dhcp Disabled + } + } catch { + Write-Output "[$env:computerName]Failed to disable DHCP on network interface 'mgmt'. Error message: $_. Exiting..." + Write-Error "[$env:computerName]Failed to disable DHCP on network interface 'mgmt'. Error message: $_. Exiting..." -ErrorAction Stop + Exit 1 + } + + try { + If (!(Get-NetIPAddress -IPAddress $dhcpIpConfig.IPv4Address.ipAddress -InterfaceAlias 'mgmt' -ErrorAction SilentlyContinue)) { + Write-Output "[$env:computerName]Setting static IP address on network interface 'mgmt'..." + New-NetIPAddress -InterfaceAlias 'mgmt' -IPAddress $dhcpIpConfig.IPv4Address.ipAddress -DefaultGateway $dhcpIpConfig.Ipv4DefaultGateway.NextHop -AddressFamily IPv4 -PrefixLength $prefixLength + } Else { + Write-Output "[$env:computerName]Static IP address already set on network interface 'mgmt'..." + } + } catch { + Write-Output "[$env:computerName]Failed to set static IP address on network interface 'mgmt'. Error message: $_. Exiting..." + Write-Error "[$env:computerName]Failed to set static IP address on network interface 'mgmt'. Error message: $_. Exiting..." -ErrorAction Stop + Exit 1 + } + + try { + Write-Output "[$env:computerName]Setting DNS server addresses on network interface 'mgmt' to '$dnsClientConfig'..." + Set-DnsClientServerAddress -InterfaceAlias 'mgmt' -ResetServerAddresses + Set-DnsClientServerAddress -InterfaceAlias 'mgmt' -ServerAddresses $dnsClientConfig + } catch { + Write-Output "[$env:computerName]Failed to set DNS server addresses on network interface 'mgmt'. Error message: $_. Exiting..." + Write-Error "[$env:computerName]Failed to set DNS server addresses on network interface 'mgmt'. Error message: $_. Exiting..." -ErrorAction Stop + Exit 1 + } +} +log "IP change output: $ipChangeOutput" diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfig.sh b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfig.sh new file mode 100644 index 0000000000..1a9948106e --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfig.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +sudo apt update +sudo apt install squid -y + +sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak +sudo cat <<EOF > /etc/squid/squid.conf + access_log /var/log/squid/access.log + + acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) + acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) + acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) + acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines + acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) + acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) + acl localnet src fc00::/7 # RFC 4193 local private network range + acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + + acl SSL_ports port 443 + acl SSL_ports port 6443 + acl SSL_ports port 8084 + + acl Safe_ports port 80 # http + acl Safe_ports port 21 # ftp + acl Safe_ports port 443 # https + acl Safe_ports port 70 # gopher + acl Safe_ports port 210 # wais + acl Safe_ports port 1025-65535 # unregistered ports + acl Safe_ports port 280 # http-mgmt + acl Safe_ports port 488 # gss-http + acl Safe_ports port 591 # filemaker + acl Safe_ports port 777 # multiling http + acl Safe_ports port 6443 + + acl HCI_Dest_URLs dstdomain .mcr.microsoft.com + acl HCI_Dest_URLs dstdomain azurearcfork8s.azurecr.io + acl HCI_Dest_URLs dstdomain linuxgeneva-microsoft.azurecr.io + acl HCI_Dest_URLs dstdomain pipelineagent.azurecr.io + acl HCI_Dest_URLs dstdomain azurearcfork8sdev.azurecr.io + acl HCI_Dest_URLs dstdomain hybridaks.azurecr.io + acl HCI_Dest_URLs dstdomain aszk8snetworking.azurecr.io + acl HCI_Dest_URLs dstdomain hybridaksstorage.z13.web.core.windows.net + # acl HCI_Dest_URLs dstdomain .dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .do.dsp.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .prod.do.dsp.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .dp.kubernetesconfiguration.azure.com + acl HCI_Dest_URLs dstdomain sts.windows.net + acl HCI_Dest_URLs dstdomain ecpacr.azurecr.io + acl HCI_Dest_URLs dstdomain pypi.org + acl HCI_Dest_URLs dstdomain files.pythonhosted.org + acl HCI_Dest_URLs dstdomain raw.githubusercontent.com + acl HCI_Dest_URLs dstdomain msk8s.api.cdp.microsoft.com + # acl HCI_Dest_URLs dstdomain msk8s.sb.tlu.dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain k8connecthelm.azureedge.net + acl HCI_Dest_URLs dstdomain kvamanagementoperator.azurecr.io + acl HCI_Dest_URLs dstdomain packages.microsoft.com + acl HCI_Dest_URLs dstdomain k8sconnectcsp.azureedge.net + acl HCI_Dest_URLs dstdomain .prod.hot.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain .dp.prod.appliances.azure.com + acl HCI_Dest_URLs dstdomain download.microsoft.com + acl HCI_Dest_URLs dstdomain pas.windows.net + acl HCI_Dest_URLs dstdomain guestnotificationservice.azure.com + acl HCI_Dest_URLs dstdomain .his.arc.azure.com + acl HCI_Dest_URLs dstdomain .guestconfiguration.azure.com + acl HCI_Dest_URLs dstdomain agentserviceapi.guestconfiguration.azure.com + acl HCI_Dest_URLs dstdomain .servicebus.windows.net + acl HCI_Dest_URLs dstdomain .waconazure.com + acl HCI_Dest_URLs dstdomain .gw.arc.azure.net + acl HCI_Dest_URLs dstdomain login.microsoftonline.com + acl HCI_Dest_URLs dstdomain graph.windows.net + acl HCI_Dest_URLs dstdomain graph.microsoft.com + acl HCI_Dest_URLs dstdomain login.windows.net + acl HCI_Dest_URLs dstdomain eastus.login.microsoft.com + acl HCI_Dest_URLs dstdomain southeastasia.login.microsoft.com + acl HCI_Dest_URLs dstdomain crl3.digicert.com + acl HCI_Dest_URLs dstdomain crl4.digicert.com + acl HCI_Dest_URLs dstdomain www.powershellgallery.com + acl HCI_Dest_URLs dstdomain psg-prod-eastus.azureedge.net + acl HCI_Dest_URLs dstdomain psg-prod-southeastasia.azureedge.net + acl HCI_Dest_URLs dstdomain onegetcdn.azureedge.net + acl HCI_Dest_URLs dstdomain portal.azure.com + acl HCI_Dest_URLs dstdomain .blob.core.windows.net + acl HCI_Dest_URLs dstdomain hciarcvmscontainerregistry.azurecr.io + acl HCI_Dest_URLs dstdomain azurestackreleases.download.prss.microsoft.com + acl HCI_Dest_URLs dstdomain settings-win.data.microsoft.com + acl HCI_Dest_URLs dstdomain dp.stackhci.azure.com + acl HCI_Dest_URLs dstdomain licensing.platform.edge.azure.com + acl HCI_Dest_URLs dstdomain billing.platform.edge.azure.com + acl HCI_Dest_URLs dstdomain azurestackhci.azurefd.net + acl HCI_Dest_URLs dstdomain .prod.microsoftmetrics.com + acl HCI_Dest_URLs dstdomain dc.services.visualstudio.com +# acl HCI_Dest_URLs dstdomain qos.prod.warm.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain .prod.warm.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain gcs.prod.monitoring.core.windows.net + acl HCI_Dest_URLs dstdomain adhs.events.data.microsoft.com + acl HCI_Dest_URLs dstdomain v20.events.data.microsoft.com + acl HCI_Dest_URLs dstdomain aka.ms + acl HCI_Dest_URLs dstdomain redirectiontool.trafficmanager.net +# acl HCI_Dest_URLs dstdomain fe3.delivery.mp.microsoft.com +# acl HCI_Dest_URLs dstdomain tlu.dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain www.microsoft.com + acl HCI_Dest_URLs dstdomain windowsupdate.microsoft.com +# acl HCI_Dest_URLs dstdomain .download.windowsupdate.com + acl HCI_Dest_URLs dstdomain wustat.windows.com + acl HCI_Dest_URLs dstdomain ntservicepack.microsoft.com + acl HCI_Dest_URLs dstdomain go.microsoft.com + acl HCI_Dest_URLs dstdomain .delivery.mp.microsoft.com + # acl HCI_Dest_URLs dstdomain .windowsupdate.microsoft.com + acl HCI_Dest_URLs dstdomain .windowsupdate.com + acl HCI_Dest_URLs dstdomain .update.microsoft.com + acl HCI_Dest_URLs dstdomain .endpoint.security.microsoft.com + acl HCI_Dest_URLs dstdomain www.office.com + acl HCI_Dest_URLs dstdomain login.microsoft.com + acl HCI_Dest_URLs dstdomain pythonhosted.org + acl HCI_Dest_URLs dstdomain .blob.storage.azure.net + acl HCI_Dest_URLs dstdomain oneocsp.microsoft.com + acl HCI_Dest_URLs dstdomain ts-crl.ws.symantec.com + acl HCI_Dest_URLs dstdomain ts-ocsp.ws.symantec.com + acl HCI_Dest_URLs dstdomain s.symcb.com + acl HCI_Dest_URLs dstdomain ocsp.digicert.com + acl HCI_Dest_URLs dstdomain ocsp2.globalsign.com + acl HCI_Dest_URLs dstdomain hciarcvmsstorage.z13.web.core.windows.net + acl HCI_Dest_URLs dstdomain management.azure.com + acl HCI_Dest_URLs dstdomain developer.microsoft.com + acl HCI_Dest_URLs dstdomain .vault.azure.net + acl HCI_Dest_URLs dstdomain .prod.hot.ingestion.msftcloudes.com # optional metrics and telemetry + acl HCI_Dest_URLs dstdomain edgesupprd.trafficmanager.net # optional support + acl HCI_Dest_URLs dstdomain .obo.arc.azure.com # optional arc - port 8084 + acl HCI_Dest_URLs dstdomain azurewatsonanalysis-prod.core.windows.net # optional observability + + acl HCI_Dest_URLs_regex dstdom_regex azgn[a-zA-Z0-9]+?\.servicebus\.windows\.net + + # used to test internet connectivity during HCI node deployment (tests NAT configuration) + acl testURL dstdomain ipinfo.io + + http_access deny !Safe_ports + + http_access deny CONNECT !SSL_ports + + http_access allow localhost manager + http_access deny manager + + http_port 3128 + + http_access allow testURL + http_access allow HCI_Dest_URLs_regex + http_access allow HCI_Dest_URLs +EOF + +sudo systemctl restart squid +sudo systemctl enable squid + +sudo ufw allow 3128/tcp diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfigArcGW.sh b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfigArcGW.sh new file mode 100644 index 0000000000..1e69299179 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e-template-assets/azureStackHCIHost/scripts/proxyConfigArcGW.sh @@ -0,0 +1,171 @@ +#!/bin/sh + +sudo apt update +sudo apt install squid -y + +sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak +sudo cat <<EOF > /etc/squid/squid.conf + access_log /var/log/squid/access.log + + acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) + acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) + acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) + acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines + acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) + acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) + acl localnet src fc00::/7 # RFC 4193 local private network range + acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + + acl SSL_ports port 443 + acl SSL_ports port 6443 + acl SSL_ports port 8084 + + acl Safe_ports port 80 # http + acl Safe_ports port 21 # ftp + acl Safe_ports port 443 # https + acl Safe_ports port 70 # gopher + acl Safe_ports port 210 # wais + acl Safe_ports port 1025-65535 # unregistered ports + acl Safe_ports port 280 # http-mgmt + acl Safe_ports port 488 # gss-http + acl Safe_ports port 591 # filemaker + acl Safe_ports port 777 # multiling http + acl Safe_ports port 6443 + + acl HCI_ResourceBridgeIPs src 172.20.0.3-172.20.0.5 + + acl HCI_Dest_URLs dstdomain .mcr.microsoft.com + acl HCI_Dest_URLs dstdomain azurearcfork8s.azurecr.io + acl HCI_Dest_URLs dstdomain linuxgeneva-microsoft.azurecr.io + acl HCI_Dest_URLs dstdomain pipelineagent.azurecr.io + acl HCI_Dest_URLs dstdomain azurearcfork8sdev.azurecr.io + acl HCI_Dest_URLs dstdomain hybridaks.azurecr.io + acl HCI_Dest_URLs dstdomain aszk8snetworking.azurecr.io + acl HCI_Dest_URLs dstdomain hybridaksstorage.z13.web.core.windows.net + # acl HCI_Dest_URLs dstdomain .dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .do.dsp.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .prod.do.dsp.mp.microsoft.com + acl HCI_Dest_URLs dstdomain .dp.kubernetesconfiguration.azure.com + acl HCI_Dest_URLs dstdomain sts.windows.net + acl HCI_Dest_URLs dstdomain ecpacr.azurecr.io + acl HCI_Dest_URLs dstdomain pypi.org + acl HCI_Dest_URLs dstdomain files.pythonhosted.org + acl HCI_Dest_URLs dstdomain raw.githubusercontent.com + acl HCI_Dest_URLs dstdomain msk8s.api.cdp.microsoft.com + # acl HCI_Dest_URLs dstdomain msk8s.sb.tlu.dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain k8connecthelm.azureedge.net + acl HCI_Dest_URLs dstdomain kvamanagementoperator.azurecr.io + acl HCI_Dest_URLs dstdomain packages.microsoft.com + acl HCI_Dest_URLs dstdomain k8sconnectcsp.azureedge.net + acl HCI_Dest_URLs dstdomain .prod.hot.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain .dp.prod.appliances.azure.com + acl HCI_Dest_URLs dstdomain download.microsoft.com + acl HCI_Dest_URLs dstdomain pas.windows.net + acl HCI_Dest_URLs dstdomain guestnotificationservice.azure.com + acl HCI_Dest_URLs dstdomain .his.arc.azure.com + acl HCI_Dest_URLs dstdomain .guestconfiguration.azure.com + acl HCI_Dest_URLs dstdomain agentserviceapi.guestconfiguration.azure.com + acl HCI_Dest_URLs dstdomain .servicebus.windows.net + acl HCI_Dest_URLs dstdomain .waconazure.com + acl HCI_Dest_URLs dstdomain .gw.arc.azure.net + acl HCI_Dest_URLs dstdomain login.microsoftonline.com + acl HCI_Dest_URLs dstdomain graph.windows.net + acl HCI_Dest_URLs dstdomain graph.microsoft.com + acl HCI_Dest_URLs dstdomain login.windows.net + acl HCI_Dest_URLs dstdomain eastus.login.microsoft.com + acl HCI_Dest_URLs dstdomain southeastasia.login.microsoft.com + acl HCI_Dest_URLs dstdomain crl3.digicert.com + acl HCI_Dest_URLs dstdomain crl4.digicert.com + acl HCI_Dest_URLs dstdomain www.powershellgallery.com + acl HCI_Dest_URLs dstdomain psg-prod-eastus.azureedge.net + acl HCI_Dest_URLs dstdomain psg-prod-southeastasia.azureedge.net + acl HCI_Dest_URLs dstdomain onegetcdn.azureedge.net + acl HCI_Dest_URLs dstdomain portal.azure.com + acl HCI_Dest_URLs dstdomain .blob.core.windows.net + acl HCI_Dest_URLs dstdomain hciarcvmscontainerregistry.azurecr.io + acl HCI_Dest_URLs dstdomain azurestackreleases.download.prss.microsoft.com + acl HCI_Dest_URLs dstdomain settings-win.data.microsoft.com + acl HCI_Dest_URLs dstdomain dp.stackhci.azure.com + acl HCI_Dest_URLs dstdomain licensing.platform.edge.azure.com + acl HCI_Dest_URLs dstdomain billing.platform.edge.azure.com + acl HCI_Dest_URLs dstdomain azurestackhci.azurefd.net + acl HCI_Dest_URLs dstdomain .prod.microsoftmetrics.com + acl HCI_Dest_URLs dstdomain dc.services.visualstudio.com +# acl HCI_Dest_URLs dstdomain qos.prod.warm.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain .prod.warm.ingest.monitor.core.windows.net + acl HCI_Dest_URLs dstdomain gcs.prod.monitoring.core.windows.net + acl HCI_Dest_URLs dstdomain adhs.events.data.microsoft.com + acl HCI_Dest_URLs dstdomain v20.events.data.microsoft.com + acl HCI_Dest_URLs dstdomain aka.ms + acl HCI_Dest_URLs dstdomain redirectiontool.trafficmanager.net +# acl HCI_Dest_URLs dstdomain fe3.delivery.mp.microsoft.com +# acl HCI_Dest_URLs dstdomain tlu.dl.delivery.mp.microsoft.com + acl HCI_Dest_URLs dstdomain www.microsoft.com + acl HCI_Dest_URLs dstdomain windowsupdate.microsoft.com +# acl HCI_Dest_URLs dstdomain .download.windowsupdate.com + acl HCI_Dest_URLs dstdomain wustat.windows.com + acl HCI_Dest_URLs dstdomain ntservicepack.microsoft.com + acl HCI_Dest_URLs dstdomain go.microsoft.com + acl HCI_Dest_URLs dstdomain .delivery.mp.microsoft.com + # acl HCI_Dest_URLs dstdomain .windowsupdate.microsoft.com + acl HCI_Dest_URLs dstdomain .windowsupdate.com + acl HCI_Dest_URLs dstdomain .update.microsoft.com + acl HCI_Dest_URLs dstdomain .endpoint.security.microsoft.com + acl HCI_Dest_URLs dstdomain www.office.com + acl HCI_Dest_URLs dstdomain login.microsoft.com + acl HCI_Dest_URLs dstdomain pythonhosted.org + acl HCI_Dest_URLs dstdomain .blob.storage.azure.net + acl HCI_Dest_URLs dstdomain oneocsp.microsoft.com + acl HCI_Dest_URLs dstdomain ts-crl.ws.symantec.com + acl HCI_Dest_URLs dstdomain ts-ocsp.ws.symantec.com + acl HCI_Dest_URLs dstdomain s.symcb.com + acl HCI_Dest_URLs dstdomain ocsp.digicert.com + acl HCI_Dest_URLs dstdomain ocsp2.globalsign.com + acl HCI_Dest_URLs dstdomain hciarcvmsstorage.z13.web.core.windows.net + acl HCI_Dest_URLs dstdomain management.azure.com + acl HCI_Dest_URLs dstdomain developer.microsoft.com + acl HCI_Dest_URLs dstdomain .vault.azure.net + acl HCI_Dest_URLs dstdomain .prod.hot.ingestion.msftcloudes.com # optional metrics and telemetry + acl HCI_Dest_URLs dstdomain edgesupprd.trafficmanager.net # optional support + acl HCI_Dest_URLs dstdomain .obo.arc.azure.com # optional arc - port 8084 + acl HCI_Dest_URLs dstdomain azurewatsonanalysis-prod.core.windows.net # optional observability + + acl HCI_Dest_URLs_regex dstdom_regex azgn[a-zA-Z0-9]+?\.servicebus\.windows\.net + + # list of URLs that are redirected to the gateway in preview see https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-azure-arc-gateway-overview#arc-enabled-server-endpoints-redirected-via-the-arc-gateway-in-limited-public-preview + acl HCI_ArcGW_Redirected_URLs dstdomain login.windows.net + acl HCI_ArcGW_Redirected_URLs dstdomain pas.windows.net + acl HCI_ArcGW_Redirected_URLs dstdomain .guestconfiguration.azure.com # Extension management and guest configuration services Always + acl HCI_ArcGW_Redirected_URLs dstdomain guestnotificationservice.azure.com # Notification service for extension and connectivity scenarios Always + #acl HCI_ArcGW_Redirected_URLs dstdomain .guestnotificationservice.azure.com # Notification service for extension and connectivity scenarios Always + acl HCI_ArcGW_Redirected_URLs dstdomain .servicesbus.windows.net # Multiple HCI services require access to this endpoint Always + acl HCI_ArcGW_Redirected_URLs dstdomain .waconazure.com # For Windows Admin Center connectivity If using Windows Admin Center + acl HCI_ArcGW_Redirected_URLs dstdomain .blob.core.windows.net # Multipsleeple HCI services require access to this endpoint Always + acl HCI_ArcGW_Redirected_URLs dstdomain dc.services.visualstudio.com # Multiple HCI services require access to this endpoint Always + + acl ArcGateway dstdomain .gw.arc.azure.com + + # used to test internet connectivity during HCI node deployment (tests NAT configuration) + acl testURL dstdomain ipinfo.io + + http_access deny !Safe_ports + + http_access deny CONNECT !SSL_ports + + http_access allow localhost manager + http_access deny manager + + http_port 3128 + + http_access allow ArcGateway + http_access allow testURL + + http_access deny HCI_ArcGW_Redirected_URLs !HCI_ResourceBridgeIPs # block redirected URLs except from the ARB + http_access allow HCI_Dest_URLs_regex + http_access allow HCI_Dest_URLs +EOF + +sudo systemctl restart squid +sudo systemctl enable squid + +sudo ufw allow 3128/tcp diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/dependencies.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..4aa097ed92 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,145 @@ +@description('Optional. The password of the LCM deployment user and local administrator accounts.') +@secure() +param deploymentUserPassword string + +@description('Required. The password of the LCM deployment user and local administrator accounts.') +@secure() +param localAdminPassword string + +@description('Required. The app ID of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentAppId string + +@description('Required. The service principal ID of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentSPObjectId string + +@description('Required. The secret of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentServicePrincipalSecret string + +@description('Required. The location to deploy the resources into.') +param location string + +@description('Required. The name of the storage account to create as a cluster witness.') +param clusterWitnessStorageAccountName string + +@description('Required. The name of the storage account to be created to collect Key Vault diagnostic logs.') +param keyVaultDiagnosticStorageAccountName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The service principal ID of the Azure Stack HCI Resource Provider in this tenant.') +@secure() +param hciResourceProviderObjectId string + +@description('Required. The name of the Azure Stack HCI cluster.') +param clusterName string + +@description('Required. The name of the VM-managed user identity to create, used for HCI Arc onboarding.') +param userAssignedIdentityName string + +@description('Required. The name of the maintenance configuration for the Azure Stack HCI Host VM and proxy server.') +param maintenanceConfigurationName string + +@description('Required. The name of the Azure VM scale set for the HCI host.') +param HCIHostVirtualMachineScaleSetName string + +@description('Conditional. The name of the Network Security Group ro create.') +param networkSecurityGroupName string + +@description('Required. The name of the virtual network to create. Used to connect the HCI Azure Host VM to an existing VNET in the same region.') +param virtualNetworkName string + +@description('Required. The name of the Network Interface Card to create.') +param networkInterfaceName string + +@description('Required. The name prefix for the Disks to create.') +param diskNamePrefix string + +@description('Required. The name of the Azure VM to create.') +param virtualMachineName string + +@description('Required. The name of the Maintenance Configuration Assignment for the proxy server.') +param maintenanceConfigurationAssignmentName string + +@description('Required. The name prefix for the \'wait\' deployment scripts to create.') +param waitDeploymentScriptPrefixName string + +var clusterNodeNames = ['hcinode1', 'hcinode2'] +var domainOUPath = 'OU=HCI,DC=hci,DC=local' +module hciHostDeployment '../../e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-hcihostdeploy' + params: { + domainOUPath: domainOUPath + hciISODownloadURL: 'https://azurestackreleases.download.prss.microsoft.com/dbazure/AzureStackHCI/OS-Composition/10.2408.0.3061/AZURESTACKHci23H2.25398.469.LCM.10.2408.0.3061.x64.en-us.iso' + hciNodeCount: length(clusterNodeNames) + hostVMSize: 'Standard_E16bds_v5' + localAdminPassword: localAdminPassword + location: location + switchlessStorageConfig: false + diskNamePrefix: diskNamePrefix + HCIHostVirtualMachineScaleSetName: HCIHostVirtualMachineScaleSetName + maintenanceConfigurationAssignmentName: maintenanceConfigurationAssignmentName + maintenanceConfigurationName: maintenanceConfigurationName + networkInterfaceName: networkInterfaceName + networkSecurityGroupName: networkSecurityGroupName + virtualNetworkName: virtualNetworkName + userAssignedIdentityName: userAssignedIdentityName + virtualMachineName: virtualMachineName + waitDeploymentScriptPrefixName: waitDeploymentScriptPrefixName + } +} + +// create the HCI cluster resource - cloudId property is needed for KeyVault secret names +resource cluster 'Microsoft.AzureStackHCI/clusters@2024-04-01' = { + name: clusterName + identity: { + type: 'SystemAssigned' + } + location: location + properties: {} +} + +module hciClusterPreqs '../../e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-hciclusterreqs' + params: { + location: location + arbDeploymentAppId: arbDeploymentAppId + arbDeploymentServicePrincipalSecret: arbDeploymentServicePrincipalSecret + arbDeploymentSPObjectId: arbDeploymentSPObjectId + arcNodeResourceIds: [ + for (nodeName, index) in clusterNodeNames: resourceId('Microsoft.HybridCompute/machines', nodeName) + ] + clusterWitnessStorageAccountName: clusterWitnessStorageAccountName + keyVaultDiagnosticStorageAccountName: keyVaultDiagnosticStorageAccountName + deploymentUsername: 'deployUser' + deploymentUserPassword: deploymentUserPassword + hciResourceProviderObjectId: hciResourceProviderObjectId + keyVaultName: keyVaultName + localAdminPassword: localAdminPassword + localAdminUsername: 'admin-hci' + logsRetentionInDays: 30 + softDeleteRetentionDays: 30 + tenantId: subscription().tenantId + vnetSubnetResourceId: hciHostDeployment.outputs.vnetSubnetResourceId + clusterName: clusterName + cloudId: cluster.properties.cloudId + } +} + +@description('The name of the created cluster') +output clusterName string = cluster.name + +@description('The name of the cluster\'s nodes.') +output clusterNodeNames array = clusterNodeNames + +@description('The name of the storage account used as the cluster witness.') +output clusterWitnessStorageAccountName string = clusterWitnessStorageAccountName + +@description('The OU path for the domain.') +output domainOUPath string = domainOUPath + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVaultName diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..e9f5b6771e --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,176 @@ +targetScope = 'subscription' + +metadata name = 'Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration' +metadata description = 'This test deploys an Azure VM to host a 2 node switched Azure Stack HCI cluster, validates the cluster configuration, and then deploys the cluster.' + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-azure-stack-hci.cluster-${serviceShort}-rg' + +@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 = 'ashc2nmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password of the LCM deployment user and local administrator accounts.') +@secure() +param localAdminAndDeploymentUserPass string = newGuid() + +@description('Required. The app ID of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentAppId string = '' + +@description('Required. The service principal ID of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentSPObjectId string = '' + +@description('Required. The secret of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentServicePrincipalSecret string = '' + +@description('Required. The service principal object ID of the Azure Stack HCI Resource Provider in this tenant. Can be fetched via `Get-AzADServicePrincipal -ApplicationId 1412d89f-b8a8-4111-b4fd-e82905cbd85d` after the \'Microsoft.AzureStackHCI\' provider was registered in the subscription.') +@secure() +#disable-next-line secure-parameter-default +param hciResourceProviderObjectId string = '' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'southeastasia' + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-nestedDependencies-${serviceShort}' + scope: resourceGroup + params: { + clusterName: '${namePrefix}${serviceShort}001' + clusterWitnessStorageAccountName: 'dep${namePrefix}wst${serviceShort}' + keyVaultDiagnosticStorageAccountName: 'dep${namePrefix}st${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + userAssignedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' + maintenanceConfigurationAssignmentName: 'dep-${namePrefix}-mca-${serviceShort}' + HCIHostVirtualMachineScaleSetName: 'dep-${namePrefix}-hvmss-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + networkInterfaceName: 'dep-${namePrefix}-mice-${serviceShort}' + diskNamePrefix: 'dep-${namePrefix}-disk-${serviceShort}' + virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}' + waitDeploymentScriptPrefixName: 'dep-${namePrefix}-wds-${serviceShort}' + arbDeploymentAppId: arbDeploymentAppId + arbDeploymentServicePrincipalSecret: arbDeploymentServicePrincipalSecret + arbDeploymentSPObjectId: arbDeploymentSPObjectId + deploymentUserPassword: localAdminAndDeploymentUserPass + hciResourceProviderObjectId: hciResourceProviderObjectId + localAdminPassword: localAdminAndDeploymentUserPass + location: enforcedLocation + } +} + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-clustermodule-${serviceShort}' + scope: resourceGroup + params: { + name: nestedDependencies.outputs.clusterName + deploymentSettings: { + customLocationName: '${namePrefix}${serviceShort}-location' + clusterNodeNames: nestedDependencies.outputs.clusterNodeNames + clusterWitnessStorageAccountName: nestedDependencies.outputs.clusterWitnessStorageAccountName + defaultGateway: '172.20.0.1' + deploymentPrefix: 'a${take(uniqueString(namePrefix, serviceShort), 7)}' // ensure deployment prefix starts with a letter to match '^(?=.{1,8}$)([a-zA-Z])(\-?[a-zA-Z\d])*$' + dnsServers: ['172.20.0.1'] + domainFqdn: 'hci.local' + domainOUPath: nestedDependencies.outputs.domainOUPath + startingIPAddress: '172.20.0.2' + endingIPAddress: '172.20.0.7' + enableStorageAutoIp: true + keyVaultName: nestedDependencies.outputs.keyVaultName + networkIntents: [ + { + adapter: ['mgmt'] + name: 'management' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Management'] + } + { + adapter: ['comp0', 'comp1'] + name: 'compute' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Compute'] + } + { + adapter: ['smb0', 'smb1'] + name: 'storage' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: true + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Storage'] + } + ] + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' + } + } +} diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..4aa097ed92 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,145 @@ +@description('Optional. The password of the LCM deployment user and local administrator accounts.') +@secure() +param deploymentUserPassword string + +@description('Required. The password of the LCM deployment user and local administrator accounts.') +@secure() +param localAdminPassword string + +@description('Required. The app ID of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentAppId string + +@description('Required. The service principal ID of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentSPObjectId string + +@description('Required. The secret of the service principal used for the Azure Stack HCI Resource Bridge deployment. If omitted, the deploying user must have permissions to create service principals and role assignments in Entra ID.') +@secure() +param arbDeploymentServicePrincipalSecret string + +@description('Required. The location to deploy the resources into.') +param location string + +@description('Required. The name of the storage account to create as a cluster witness.') +param clusterWitnessStorageAccountName string + +@description('Required. The name of the storage account to be created to collect Key Vault diagnostic logs.') +param keyVaultDiagnosticStorageAccountName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The service principal ID of the Azure Stack HCI Resource Provider in this tenant.') +@secure() +param hciResourceProviderObjectId string + +@description('Required. The name of the Azure Stack HCI cluster.') +param clusterName string + +@description('Required. The name of the VM-managed user identity to create, used for HCI Arc onboarding.') +param userAssignedIdentityName string + +@description('Required. The name of the maintenance configuration for the Azure Stack HCI Host VM and proxy server.') +param maintenanceConfigurationName string + +@description('Required. The name of the Azure VM scale set for the HCI host.') +param HCIHostVirtualMachineScaleSetName string + +@description('Conditional. The name of the Network Security Group ro create.') +param networkSecurityGroupName string + +@description('Required. The name of the virtual network to create. Used to connect the HCI Azure Host VM to an existing VNET in the same region.') +param virtualNetworkName string + +@description('Required. The name of the Network Interface Card to create.') +param networkInterfaceName string + +@description('Required. The name prefix for the Disks to create.') +param diskNamePrefix string + +@description('Required. The name of the Azure VM to create.') +param virtualMachineName string + +@description('Required. The name of the Maintenance Configuration Assignment for the proxy server.') +param maintenanceConfigurationAssignmentName string + +@description('Required. The name prefix for the \'wait\' deployment scripts to create.') +param waitDeploymentScriptPrefixName string + +var clusterNodeNames = ['hcinode1', 'hcinode2'] +var domainOUPath = 'OU=HCI,DC=hci,DC=local' +module hciHostDeployment '../../e2e-template-assets/azureStackHCIHost/hciHostDeployment.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-hcihostdeploy' + params: { + domainOUPath: domainOUPath + hciISODownloadURL: 'https://azurestackreleases.download.prss.microsoft.com/dbazure/AzureStackHCI/OS-Composition/10.2408.0.3061/AZURESTACKHci23H2.25398.469.LCM.10.2408.0.3061.x64.en-us.iso' + hciNodeCount: length(clusterNodeNames) + hostVMSize: 'Standard_E16bds_v5' + localAdminPassword: localAdminPassword + location: location + switchlessStorageConfig: false + diskNamePrefix: diskNamePrefix + HCIHostVirtualMachineScaleSetName: HCIHostVirtualMachineScaleSetName + maintenanceConfigurationAssignmentName: maintenanceConfigurationAssignmentName + maintenanceConfigurationName: maintenanceConfigurationName + networkInterfaceName: networkInterfaceName + networkSecurityGroupName: networkSecurityGroupName + virtualNetworkName: virtualNetworkName + userAssignedIdentityName: userAssignedIdentityName + virtualMachineName: virtualMachineName + waitDeploymentScriptPrefixName: waitDeploymentScriptPrefixName + } +} + +// create the HCI cluster resource - cloudId property is needed for KeyVault secret names +resource cluster 'Microsoft.AzureStackHCI/clusters@2024-04-01' = { + name: clusterName + identity: { + type: 'SystemAssigned' + } + location: location + properties: {} +} + +module hciClusterPreqs '../../e2e-template-assets/azureStackHCIClusterPreqs/ashciPrereqs.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-hciclusterreqs' + params: { + location: location + arbDeploymentAppId: arbDeploymentAppId + arbDeploymentServicePrincipalSecret: arbDeploymentServicePrincipalSecret + arbDeploymentSPObjectId: arbDeploymentSPObjectId + arcNodeResourceIds: [ + for (nodeName, index) in clusterNodeNames: resourceId('Microsoft.HybridCompute/machines', nodeName) + ] + clusterWitnessStorageAccountName: clusterWitnessStorageAccountName + keyVaultDiagnosticStorageAccountName: keyVaultDiagnosticStorageAccountName + deploymentUsername: 'deployUser' + deploymentUserPassword: deploymentUserPassword + hciResourceProviderObjectId: hciResourceProviderObjectId + keyVaultName: keyVaultName + localAdminPassword: localAdminPassword + localAdminUsername: 'admin-hci' + logsRetentionInDays: 30 + softDeleteRetentionDays: 30 + tenantId: subscription().tenantId + vnetSubnetResourceId: hciHostDeployment.outputs.vnetSubnetResourceId + clusterName: clusterName + cloudId: cluster.properties.cloudId + } +} + +@description('The name of the created cluster') +output clusterName string = cluster.name + +@description('The name of the cluster\'s nodes.') +output clusterNodeNames array = clusterNodeNames + +@description('The name of the storage account used as the cluster witness.') +output clusterWitnessStorageAccountName string = clusterWitnessStorageAccountName + +@description('The OU path for the domain.') +output domainOUPath string = domainOUPath + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVaultName diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..5fd4a0c051 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,187 @@ +targetScope = 'subscription' + +metadata name = 'Deploy Azure Stack HCI Cluster in Azure with a 2 node switched configuration WAF aligned' +metadata description = 'This test deploys an Azure VM to host a 2 node switched Azure Stack HCI cluster, validates the cluster configuration, and then deploys the cluster WAF aligned.' + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-azure-stack-hci.cluster-${serviceShort}-rg' + +@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 = 'ashc2nwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password of the LCM deployment user and local administrator accounts.') +@secure() +param localAdminAndDeploymentUserPass string = newGuid() + +@description('Required. The app ID of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentAppId string = '' + +@description('Required. The service principal ID of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentSPObjectId string = '' + +@description('Required. The secret of the service principal used for the Azure Stack HCI Resource Bridge deployment.') +@secure() +#disable-next-line secure-parameter-default +param arbDeploymentServicePrincipalSecret string = '' + +@description('Required. The service principal object ID of the Azure Stack HCI Resource Provider in this tenant. Can be fetched via `Get-AzADServicePrincipal -ApplicationId 1412d89f-b8a8-4111-b4fd-e82905cbd85d` after the \'Microsoft.AzureStackHCI\' provider was registered in the subscription.') +@secure() +#disable-next-line secure-parameter-default +param hciResourceProviderObjectId string = '' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'southeastasia' + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-nestedDependencies-${serviceShort}' + scope: resourceGroup + params: { + clusterName: '${namePrefix}${serviceShort}001' + clusterWitnessStorageAccountName: 'dep${namePrefix}wst${serviceShort}' + keyVaultDiagnosticStorageAccountName: 'dep${namePrefix}st${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + userAssignedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' + maintenanceConfigurationAssignmentName: 'dep-${namePrefix}-mca-${serviceShort}' + HCIHostVirtualMachineScaleSetName: 'dep-${namePrefix}-hvmss-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + networkInterfaceName: 'dep-${namePrefix}-mice-${serviceShort}' + diskNamePrefix: 'dep-${namePrefix}-disk-${serviceShort}' + virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}' + waitDeploymentScriptPrefixName: 'dep-${namePrefix}-wds-${serviceShort}' + arbDeploymentAppId: arbDeploymentAppId + arbDeploymentServicePrincipalSecret: arbDeploymentServicePrincipalSecret + arbDeploymentSPObjectId: arbDeploymentSPObjectId + deploymentUserPassword: localAdminAndDeploymentUserPass + hciResourceProviderObjectId: hciResourceProviderObjectId + localAdminPassword: localAdminAndDeploymentUserPass + location: enforcedLocation + } +} + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-clustermodule-${serviceShort}' + scope: resourceGroup + params: { + name: nestedDependencies.outputs.clusterName + deploymentSettings: { + customLocationName: '${namePrefix}${serviceShort}-location' + clusterNodeNames: nestedDependencies.outputs.clusterNodeNames + clusterWitnessStorageAccountName: nestedDependencies.outputs.clusterWitnessStorageAccountName + defaultGateway: '172.20.0.1' + deploymentPrefix: 'a${take(uniqueString(namePrefix, serviceShort), 7)}' // ensure deployment prefix starts with a letter to match '^(?=.{1,8}$)([a-zA-Z])(\-?[a-zA-Z\d])*$' + dnsServers: ['172.20.0.1'] + domainFqdn: 'hci.local' + domainOUPath: nestedDependencies.outputs.domainOUPath + startingIPAddress: '172.20.0.2' + endingIPAddress: '172.20.0.7' + enableStorageAutoIp: true + keyVaultName: nestedDependencies.outputs.keyVaultName + networkIntents: [ + { + adapter: ['mgmt'] + name: 'management' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Management'] + } + { + adapter: ['comp0', 'comp1'] + name: 'compute' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: false + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Compute'] + } + { + adapter: ['smb0', 'smb1'] + name: 'storage' + overrideAdapterProperty: true + adapterPropertyOverrides: { + jumboPacket: '9014' + networkDirect: 'Disabled' + networkDirectTechnology: 'iWARP' + } + overrideQosPolicy: true + qosPolicyOverrides: { + bandwidthPercentage_SMB: '50' + priorityValue8021Action_Cluster: '7' + priorityValue8021Action_SMB: '3' + } + overrideVirtualSwitchConfiguration: false + virtualSwitchConfigurationOverrides: { + enableIov: 'true' + loadBalancingAlgorithm: 'Dynamic' + } + trafficType: ['Storage'] + } + ] + storageConnectivitySwitchless: false + storageNetworks: [ + { + adapterName: 'smb0' + vlan: '711' + } + { + adapterName: 'smb1' + vlan: '712' + } + ] + subnetMask: '255.255.255.0' + driftControlEnforced: true + smbSigningEnforced: true + smbClusterEncryption: true + sideChannelMitigationEnforced: true + bitlockerBootVolume: true + bitlockerDataVolumes: true + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/azure-stack-hci/cluster/version.json b/avm/res/azure-stack-hci/cluster/version.json new file mode 100644 index 0000000000..1c884ecaa9 --- /dev/null +++ b/avm/res/azure-stack-hci/cluster/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file From 1352aa7c7350d5d67ceb4993ca24a7060ed52589 Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Wed, 12 Feb 2025 16:46:03 +0100 Subject: [PATCH 24/53] fix: Shortened test resource name for HCI cluster (#4436) ## Description Shortened test resource name for HCI cluster as it runs into a length issue (16 chars vs max of 15 chars) during test deployment No version update required as it only afffects test resources. ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --- avm/res/azure-stack-hci/cluster/README.md | 12 ++++++------ .../cluster/deployment-setting/main.json | 4 ++-- avm/res/azure-stack-hci/cluster/main.json | 8 ++++---- .../cluster/tests/e2e/defaults/main.test.bicep | 4 ++-- .../cluster/tests/e2e/waf-aligned/main.test.bicep | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/avm/res/azure-stack-hci/cluster/README.md b/avm/res/azure-stack-hci/cluster/README.md index 4cb635ad3f..3576848736 100644 --- a/avm/res/azure-stack-hci/cluster/README.md +++ b/avm/res/azure-stack-hci/cluster/README.md @@ -49,7 +49,7 @@ module cluster 'br/public:avm/res/azure-stack-hci/cluster:<version>' = { deploymentSettings: { clusterNodeNames: '<clusterNodeNames>' clusterWitnessStorageAccountName: '<clusterWitnessStorageAccountName>' - customLocationName: 'ashc2nmin-location' + customLocationName: 'ashcmin-location' defaultGateway: '172.20.0.1' deploymentPrefix: '<deploymentPrefix>' dnsServers: [ @@ -181,7 +181,7 @@ module cluster 'br/public:avm/res/azure-stack-hci/cluster:<version>' = { "value": { "clusterNodeNames": "<clusterNodeNames>", "clusterWitnessStorageAccountName": "<clusterWitnessStorageAccountName>", - "customLocationName": "ashc2nmin-location", + "customLocationName": "ashcmin-location", "defaultGateway": "172.20.0.1", "deploymentPrefix": "<deploymentPrefix>", "dnsServers": [ @@ -309,7 +309,7 @@ param name = '<name>' param deploymentSettings = { clusterNodeNames: '<clusterNodeNames>' clusterWitnessStorageAccountName: '<clusterWitnessStorageAccountName>' - customLocationName: 'ashc2nmin-location' + customLocationName: 'ashcmin-location' defaultGateway: '172.20.0.1' deploymentPrefix: '<deploymentPrefix>' dnsServers: [ @@ -442,7 +442,7 @@ module cluster 'br/public:avm/res/azure-stack-hci/cluster:<version>' = { bitlockerDataVolumes: true clusterNodeNames: '<clusterNodeNames>' clusterWitnessStorageAccountName: '<clusterWitnessStorageAccountName>' - customLocationName: 'ashc2nwaf-location' + customLocationName: 'ashcwaf-location' defaultGateway: '172.20.0.1' deploymentPrefix: '<deploymentPrefix>' dnsServers: [ @@ -585,7 +585,7 @@ module cluster 'br/public:avm/res/azure-stack-hci/cluster:<version>' = { "bitlockerDataVolumes": true, "clusterNodeNames": "<clusterNodeNames>", "clusterWitnessStorageAccountName": "<clusterWitnessStorageAccountName>", - "customLocationName": "ashc2nwaf-location", + "customLocationName": "ashcwaf-location", "defaultGateway": "172.20.0.1", "deploymentPrefix": "<deploymentPrefix>", "dnsServers": [ @@ -726,7 +726,7 @@ param deploymentSettings = { bitlockerDataVolumes: true clusterNodeNames: '<clusterNodeNames>' clusterWitnessStorageAccountName: '<clusterWitnessStorageAccountName>' - customLocationName: 'ashc2nwaf-location' + customLocationName: 'ashcwaf-location' defaultGateway: '172.20.0.1' deploymentPrefix: '<deploymentPrefix>' dnsServers: [ diff --git a/avm/res/azure-stack-hci/cluster/deployment-setting/main.json b/avm/res/azure-stack-hci/cluster/deployment-setting/main.json index 2edb982bfa..de60f2e4ac 100644 --- a/avm/res/azure-stack-hci/cluster/deployment-setting/main.json +++ b/avm/res/azure-stack-hci/cluster/deployment-setting/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7982158538530558379" + "version": "0.33.13.18514", + "templateHash": "10232132824907554113" }, "name": "Azure Stack HCI Cluster Deployment Settings", "description": "This module deploys an Azure Stack HCI Cluster Deployment Settings resource." diff --git a/avm/res/azure-stack-hci/cluster/main.json b/avm/res/azure-stack-hci/cluster/main.json index f082101b8d..0a2481719a 100644 --- a/avm/res/azure-stack-hci/cluster/main.json +++ b/avm/res/azure-stack-hci/cluster/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2661952410917464461" + "version": "0.33.13.18514", + "templateHash": "2800706656796817324" }, "name": "Azure Stack HCI Cluster", "description": "This module deploys an Azure Stack HCI Cluster on the provided Arc Machines." @@ -837,8 +837,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7982158538530558379" + "version": "0.33.13.18514", + "templateHash": "10232132824907554113" }, "name": "Azure Stack HCI Cluster Deployment Settings", "description": "This module deploys an Azure Stack HCI Cluster Deployment Settings resource." diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep index e9f5b6771e..4bd69b5dcf 100644 --- a/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/defaults/main.test.bicep @@ -8,7 +8,7 @@ metadata description = 'This test deploys an Azure VM to host a 2 node switched param resourceGroupName string = 'dep-${namePrefix}-azure-stack-hci.cluster-${serviceShort}-rg' @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 = 'ashc2nmin' +param serviceShort string = 'ashcmin' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -49,7 +49,7 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, enforcedLocation)}-test-nestedDependencies-${serviceShort}' scope: resourceGroup params: { - clusterName: '${namePrefix}${serviceShort}001' + clusterName: '${namePrefix}${serviceShort}1' clusterWitnessStorageAccountName: 'dep${namePrefix}wst${serviceShort}' keyVaultDiagnosticStorageAccountName: 'dep${namePrefix}st${serviceShort}' keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' diff --git a/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep index 5fd4a0c051..131c3d6769 100644 --- a/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/azure-stack-hci/cluster/tests/e2e/waf-aligned/main.test.bicep @@ -8,7 +8,7 @@ metadata description = 'This test deploys an Azure VM to host a 2 node switched param resourceGroupName string = 'dep-${namePrefix}-azure-stack-hci.cluster-${serviceShort}-rg' @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 = 'ashc2nwaf' +param serviceShort string = 'ashcwaf' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -49,7 +49,7 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, enforcedLocation)}-test-nestedDependencies-${serviceShort}' scope: resourceGroup params: { - clusterName: '${namePrefix}${serviceShort}001' + clusterName: '${namePrefix}${serviceShort}1' clusterWitnessStorageAccountName: 'dep${namePrefix}wst${serviceShort}' keyVaultDiagnosticStorageAccountName: 'dep${namePrefix}st${serviceShort}' keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' From c9f75cd44518056e8cf4fead5c8d74373f03df22 Mon Sep 17 00:00:00 2001 From: Joe Linn <120408555+jlinn-microsoft@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:04:38 -0800 Subject: [PATCH 25/53] feat: New module `avm/res/app/session-pool` module. (#4178) ## Description <!-- >Thank you for your contribution ! > Please include a summary of the change and which issue is fixed. > Please also include the context. > List any dependencies that are required for this change. Fixes #123 Fixes #456 Closes #123 Closes #456 --> Adds the `avm/res/app/session-pool` module. Closes Azure/Azure-Verified-Modules#1764 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.app.session-pool](https://github.com/jlinn-microsoft/bicep-registry-modules/actions/workflows/avm.res.app.session-pool.yml/badge.svg)](https://github.com/jlinn-microsoft/bicep-registry-modules/actions/workflows/avm.res.app.session-pool.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../workflows/avm.res.app.session-pool.yml | 88 ++ avm/res/app/session-pool/README.md | 878 ++++++++++++++++++ avm/res/app/session-pool/main.bicep | 283 ++++++ avm/res/app/session-pool/main.json | 601 ++++++++++++ .../tests/e2e/defaults/main.test.bicep | 50 + .../tests/e2e/max/dependencies.bicep | 16 + .../tests/e2e/max/main.test.bicep | 84 ++ .../tests/e2e/waf-aligned/main.test.bicep | 54 ++ avm/res/app/session-pool/version.json | 7 + 11 files changed, 2063 insertions(+) create mode 100644 .github/workflows/avm.res.app.session-pool.yml create mode 100644 avm/res/app/session-pool/README.md create mode 100644 avm/res/app/session-pool/main.bicep create mode 100644 avm/res/app/session-pool/main.json create mode 100644 avm/res/app/session-pool/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/app/session-pool/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/app/session-pool/tests/e2e/max/main.test.bicep create mode 100644 avm/res/app/session-pool/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/app/session-pool/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6f48d83936..2b6613a51b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -43,6 +43,7 @@ /avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/app/job/ @Azure/avm-res-app-job-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/app/session-pool/ @Azure/avm-res-app-sessionpool-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/automation/automation-account/ @Azure/avm-res-automation-automationaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/azure-stack-hci/cluster/ @Azure/avm-res-azurestackhci-cluster-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index b79261069b..09a2c5ab4d 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -79,6 +79,7 @@ body: - "avm/res/app/container-app" - "avm/res/app/job" - "avm/res/app/managed-environment" + - "avm/res/app/session-pool" - "avm/res/automation/automation-account" - "avm/res/azure-stack-hci/cluster" - "avm/res/batch/batch-account" diff --git a/.github/workflows/avm.res.app.session-pool.yml b/.github/workflows/avm.res.app.session-pool.yml new file mode 100644 index 0000000000..4ab8010119 --- /dev/null +++ b/.github/workflows/avm.res.app.session-pool.yml @@ -0,0 +1,88 @@ +name: "avm.res.app.session-pool" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.app.session-pool.yml" + - "avm/res/app/session-pool/**" + - "utilities/pipelines/**" + - "!utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/app/session-pool" + workflowPath: ".github/workflows/avm.res.app.session-pool.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/app/session-pool/README.md b/avm/res/app/session-pool/README.md new file mode 100644 index 0000000000..c4b1b1db46 --- /dev/null +++ b/avm/res/app/session-pool/README.md @@ -0,0 +1,878 @@ +# Container App Session Pool `[Microsoft.App/sessionPools]` + +This module deploys a Container App Session Pool. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.App/sessionPools` | [2024-10-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-10-02-preview/sessionPools) | +| `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) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/app/session-pool:<version>`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module sessionPool 'br/public:avm/res/app/session-pool:<version>' = { + name: 'sessionPoolDeployment' + params: { + // Required parameters + containerType: 'PythonLTS' + name: 'aspmin001' + // Non-required parameters + location: '<location>' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containerType": { + "value": "PythonLTS" + }, + "name": { + "value": "aspmin001" + }, + // Non-required parameters + "location": { + "value": "<location>" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/app/session-pool:<version>' + +// Required parameters +param containerType = 'PythonLTS' +param name = 'aspmin001' +// Non-required parameters +param location = '<location>' +``` + +</details> +<p> + +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module sessionPool 'br/public:avm/res/app/session-pool:<version>' = { + name: 'sessionPoolDeployment' + params: { + // Required parameters + containerType: 'PythonLTS' + name: 'aspmax001' + // Non-required parameters + cooldownPeriodInSeconds: 350 + location: '<location>' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentitySettings: [ + { + identity: '<identity>' + lifecycle: 'Main' + } + ] + maxConcurrentSessions: 6 + poolManagementType: 'Dynamic' + readySessionInstances: 1 + roleAssignments: [ + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Azure ContainerApps Session Executor' + } + ] + sessionNetworkStatus: 'EgressDisabled' + tags: { + resourceType: 'Session Pool' + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containerType": { + "value": "PythonLTS" + }, + "name": { + "value": "aspmax001" + }, + // Non-required parameters + "cooldownPeriodInSeconds": { + "value": 350 + }, + "location": { + "value": "<location>" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentitySettings": { + "value": [ + { + "identity": "<identity>", + "lifecycle": "Main" + } + ] + }, + "maxConcurrentSessions": { + "value": 6 + }, + "poolManagementType": { + "value": "Dynamic" + }, + "readySessionInstances": { + "value": 1 + }, + "roleAssignments": { + "value": [ + { + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Azure ContainerApps Session Executor" + } + ] + }, + "sessionNetworkStatus": { + "value": "EgressDisabled" + }, + "tags": { + "value": { + "resourceType": "Session Pool" + } + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/app/session-pool:<version>' + +// Required parameters +param containerType = 'PythonLTS' +param name = 'aspmax001' +// Non-required parameters +param cooldownPeriodInSeconds = 350 +param location = '<location>' +param lock = { + kind: 'CanNotDelete' + name: 'myCustomLockName' +} +param managedIdentitySettings = [ + { + identity: '<identity>' + lifecycle: 'Main' + } +] +param maxConcurrentSessions = 6 +param poolManagementType = 'Dynamic' +param readySessionInstances = 1 +param roleAssignments = [ + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Azure ContainerApps Session Executor' + } +] +param sessionNetworkStatus = 'EgressDisabled' +param tags = { + resourceType: 'Session Pool' +} +``` + +</details> +<p> + +### Example 3: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module sessionPool 'br/public:avm/res/app/session-pool:<version>' = { + name: 'sessionPoolDeployment' + params: { + // Required parameters + containerType: 'PythonLTS' + name: 'aspwaf001' + // Non-required parameters + location: '<location>' + sessionNetworkStatus: 'EgressDisabled' + tags: { + resourceType: 'Session Pool' + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containerType": { + "value": "PythonLTS" + }, + "name": { + "value": "aspwaf001" + }, + // Non-required parameters + "location": { + "value": "<location>" + }, + "sessionNetworkStatus": { + "value": "EgressDisabled" + }, + "tags": { + "value": { + "resourceType": "Session Pool" + } + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/app/session-pool:<version>' + +// Required parameters +param containerType = 'PythonLTS' +param name = 'aspwaf001' +// Non-required parameters +param location = '<location>' +param sessionNetworkStatus = 'EgressDisabled' +param tags = { + resourceType: 'Session Pool' +} +``` + +</details> +<p> + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`containerType`](#parameter-containertype) | string | The container type of the sessions. | +| [`name`](#parameter-name) | string | Name of the Container App Session Pool. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`containers`](#parameter-containers) | array | Custom container definitions. Only required if containerType is CustomContainer. | +| [`cooldownPeriodInSeconds`](#parameter-cooldownperiodinseconds) | int | The cooldown period of a session in seconds. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`environmentId`](#parameter-environmentid) | string | Resource ID of the session pool's environment. | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`managedIdentitySettings`](#parameter-managedidentitysettings) | array | Settings for a Managed Identity that is assigned to the Session pool. | +| [`maxConcurrentSessions`](#parameter-maxconcurrentsessions) | int | The maximum count of sessions at the same time. | +| [`poolManagementType`](#parameter-poolmanagementtype) | string | The pool management type of the session pool. Defaults to Dynamic. | +| [`readySessionInstances`](#parameter-readysessioninstances) | int | The minimum count of ready session instances. | +| [`registryCredentials`](#parameter-registrycredentials) | object | Container registry credentials. Only required if containerType is CustomContainer and the container registry requires authentication. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`sessionNetworkStatus`](#parameter-sessionnetworkstatus) | string | Network status for the sessions. Defaults to EgressDisabled. | +| [`tags`](#parameter-tags) | object | Tags of the Automation Account resource. | +| [`targetIngressPort`](#parameter-targetingressport) | int | Required if containerType == 'CustomContainer'. Target port in containers for traffic from ingress. Only required if containerType is CustomContainer. | + +### Parameter: `containerType` + +The container type of the sessions. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'CustomContainer' + 'PythonLTS' + ] + ``` + +### Parameter: `name` + +Name of the Container App Session Pool. + +- Required: Yes +- Type: string + +### Parameter: `containers` + +Custom container definitions. Only required if containerType is CustomContainer. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`image`](#parameter-containersimage) | string | Container image tag. | +| [`name`](#parameter-containersname) | string | Custom container name. | +| [`resources`](#parameter-containersresources) | object | Container resource requirements. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`args`](#parameter-containersargs) | array | Container start command arguments. | +| [`command`](#parameter-containerscommand) | array | Container start command. | +| [`env`](#parameter-containersenv) | array | Container environment variables. | + +### Parameter: `containers.image` + +Container image tag. + +- Required: Yes +- Type: string + +### Parameter: `containers.name` + +Custom container name. + +- Required: Yes +- Type: string + +### Parameter: `containers.resources` + +Container resource requirements. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cpu`](#parameter-containersresourcescpu) | string | Required CPU in cores, e.g. 0.5. | +| [`memory`](#parameter-containersresourcesmemory) | string | Required memory, e.g. "1.25Gi". | + +### Parameter: `containers.resources.cpu` + +Required CPU in cores, e.g. 0.5. + +- Required: Yes +- Type: string + +### Parameter: `containers.resources.memory` + +Required memory, e.g. "1.25Gi". + +- Required: Yes +- Type: string + +### Parameter: `containers.args` + +Container start command arguments. + +- Required: No +- Type: array + +### Parameter: `containers.command` + +Container start command. + +- Required: No +- Type: array + +### Parameter: `containers.env` + +Container environment variables. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-containersenvname) | string | Environment variable name. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secretRef`](#parameter-containersenvsecretref) | string | Required if value is not set. Name of the Container App secret from which to pull the environment variable value. | +| [`value`](#parameter-containersenvvalue) | string | Required if secretRef is not set. Non-secret environment variable value. | + +### Parameter: `containers.env.name` + +Environment variable name. + +- Required: Yes +- Type: string + +### Parameter: `containers.env.secretRef` + +Required if value is not set. Name of the Container App secret from which to pull the environment variable value. + +- Required: No +- Type: string + +### Parameter: `containers.env.value` + +Required if secretRef is not set. Non-secret environment variable value. + +- Required: No +- Type: string + +### Parameter: `cooldownPeriodInSeconds` + +The cooldown period of a session in seconds. + +- Required: No +- Type: int +- Default: `300` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `environmentId` + +Resource ID of the session pool's environment. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. + +- Required: No +- Type: array + +### Parameter: `managedIdentitySettings` + +Settings for a Managed Identity that is assigned to the Session pool. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`identity`](#parameter-managedidentitysettingsidentity) | string | The resource ID of a user-assigned managed identity that is assigned to the Session Pool, or "system" for system-assigned identity. | +| [`lifecycle`](#parameter-managedidentitysettingslifecycle) | string | Use to select the lifecycle stages of a Session Pool during which the Managed Identity should be available. Valid values: "All", "Init", "Main", "None". | + +### Parameter: `managedIdentitySettings.identity` + +The resource ID of a user-assigned managed identity that is assigned to the Session Pool, or "system" for system-assigned identity. + +- Required: Yes +- Type: string + +### Parameter: `managedIdentitySettings.lifecycle` + +Use to select the lifecycle stages of a Session Pool during which the Managed Identity should be available. Valid values: "All", "Init", "Main", "None". + +- Required: Yes +- Type: string + +### Parameter: `maxConcurrentSessions` + +The maximum count of sessions at the same time. + +- Required: No +- Type: int +- Default: `5` + +### Parameter: `poolManagementType` + +The pool management type of the session pool. Defaults to Dynamic. + +- Required: No +- Type: string +- Default: `'Dynamic'` +- Allowed: + ```Bicep + [ + 'Dynamic' + 'Manual' + ] + ``` + +### Parameter: `readySessionInstances` + +The minimum count of ready session instances. + +- Required: No +- Type: int + +### Parameter: `registryCredentials` + +Container registry credentials. Only required if containerType is CustomContainer and the container registry requires authentication. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`server`](#parameter-registrycredentialsserver) | string | Container registry server. | +| [`username`](#parameter-registrycredentialsusername) | string | Container registry username. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`identity`](#parameter-registrycredentialsidentity) | string | A Managed Identity to use to authenticate with Azure Container Registry. For user-assigned identities, use the full user-assigned identity Resource ID. For system-assigned identities, use "system". | +| [`passwordSecretRef`](#parameter-registrycredentialspasswordsecretref) | string | The name of the secret that contains the registry login password. Not used if identity is specified. | + +### Parameter: `registryCredentials.server` + +Container registry server. + +- Required: Yes +- Type: string + +### Parameter: `registryCredentials.username` + +Container registry username. + +- Required: Yes +- Type: string + +### Parameter: `registryCredentials.identity` + +A Managed Identity to use to authenticate with Azure Container Registry. For user-assigned identities, use the full user-assigned identity Resource ID. For system-assigned identities, use "system". + +- Required: No +- Type: string + +### Parameter: `registryCredentials.passwordSecretRef` + +The name of the secret that contains the registry login password. Not used if identity is specified. + +- Required: No +- Type: string + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Azure ContainerApps Session Executor'` + - `'Container Apps SessionPools Contributor'` + - `'Container Apps SessionPools Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `sessionNetworkStatus` + +Network status for the sessions. Defaults to EgressDisabled. + +- Required: No +- Type: string +- Default: `'EgressDisabled'` +- Allowed: + ```Bicep + [ + 'EgressDisabled' + 'EgressEnabled' + ] + ``` + +### Parameter: `tags` + +Tags of the Automation Account resource. + +- Required: No +- Type: object + +### Parameter: `targetIngressPort` + +Required if containerType == 'CustomContainer'. Target port in containers for traffic from ingress. Only required if containerType is CustomContainer. + +- Required: No +- Type: int + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `managementEndpoint` | string | The management endpoint of the session pool. | +| `name` | string | The name of the session pool. | +| `resourceGroupName` | string | The name of the resource group in which the session pool was created. | +| `resourceId` | string | The resource ID of the deployed session pool. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.2.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/app/session-pool/main.bicep b/avm/res/app/session-pool/main.bicep new file mode 100644 index 0000000000..bd37c80d76 --- /dev/null +++ b/avm/res/app/session-pool/main.bicep @@ -0,0 +1,283 @@ +metadata name = 'Container App Session Pool' +metadata description = 'This module deploys a Container App Session Pool.' + +@description('Required. Name of the Container App Session Pool.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Required. The container type of the sessions.') +@allowed(['PythonLTS', 'CustomContainer']) +param containerType string + +@description('Optional. Custom container definitions. Only required if containerType is CustomContainer.') +param containers sessionContainerType[]? + +@description('Optional. Required if containerType == \'CustomContainer\'. Target port in containers for traffic from ingress. Only required if containerType is CustomContainer.') +param targetIngressPort int? + +@description('Optional. Container registry credentials. Only required if containerType is CustomContainer and the container registry requires authentication.') +param registryCredentials sessionRegistryCredentialsType? + +@description('Optional. The cooldown period of a session in seconds.') +param cooldownPeriodInSeconds int = 300 + +@description('Optional. The maximum count of sessions at the same time.') +param maxConcurrentSessions int = 5 + +@description('Optional. The minimum count of ready session instances.') +param readySessionInstances int? + +@description('Optional. Network status for the sessions. Defaults to EgressDisabled.') +@allowed(['EgressEnabled', 'EgressDisabled']) +param sessionNetworkStatus string = 'EgressDisabled' + +@description('Optional. The pool management type of the session pool. Defaults to Dynamic.') +@allowed(['Dynamic', 'Manual']) +param poolManagementType string = 'Dynamic' + +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.2.1' +@description('Optional. The lock settings of the service.') +param lock lockType? + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.2.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.2.1' +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentityAllType? + +@description('Optional. Settings for a Managed Identity that is assigned to the Session pool.') +param managedIdentitySettings managedIdentitySettingType[]? + +@description('Optional. Resource ID of the session pool\'s environment.') +param environmentId string? + +@description('Optional. Tags of the Automation Account resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (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.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +var builtInRoleNames = { + 'Azure ContainerApps Session Executor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '0fb8eba5-a2bb-4abe-b1c1-49dfad359bb0' + ) + 'Container Apps SessionPools Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f7669afb-68b2-44b4-9c5f-6d2a47fddda0' + ) + 'Container Apps SessionPools Reader': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'af61e8fc-2633-4b95-bed3-421ad6826515' + ) + 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': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.app-sessionpool.${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: '#_moduleVersion_#.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource sessionPool 'Microsoft.App/sessionPools@2024-10-02-preview' = { + name: name + location: location + identity: identity + properties: { + containerType: containerType + environmentId: environmentId + customContainerTemplate: containerType == 'CustomContainer' + ? { + containers: containers + ingress: { + targetPort: targetIngressPort + } + registryCredentials: registryCredentials + } + : null + dynamicPoolConfiguration: { + cooldownPeriodInSeconds: cooldownPeriodInSeconds + executionType: 'Timed' + } + managedIdentitySettings: managedIdentitySettings + scaleConfiguration: { + maxConcurrentSessions: maxConcurrentSessions + readySessionInstances: readySessionInstances + } + sessionNetworkConfiguration: { + status: sessionNetworkStatus + } + poolManagementType: poolManagementType + } + tags: tags +} + +resource sessionPool_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: sessionPool +} + +resource sessionPool_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(sessionPool.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: sessionPool + } +] + +@description('The name of the session pool.') +output name string = sessionPool.name + +@description('The resource ID of the deployed session pool.') +output resourceId string = sessionPool.id + +@description('The name of the resource group in which the session pool was created.') +output resourceGroupName string = resourceGroup().name + +@description('The management endpoint of the session pool.') +output managementEndpoint string = sessionPool.properties.poolManagementEndpoint + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = sessionPool.?identity.?principalId ?? '' + +// =============== // +// Definitions // +// =============== // + +@export() +@description('Optional. Custom container definition.') +type sessionContainerType = { + @description('Optional. Container start command arguments.') + args: string[]? + + @description('Optional. Container start command.') + command: string[]? + + @description('Optional. Container environment variables.') + env: sessionContainerEnvType[]? + + @description('Required. Container image tag.') + image: string + + @description('Required. Custom container name.') + name: string + + @description('Required. Container resource requirements.') + resources: sessionContainerResourceType +} + +@export() +@description('Optional. Environment variable definition for a container. Only used with custom containers.') +type sessionContainerEnvType = { + @description('Required. Environment variable name.') + name: string + + @description('Optional. Required if value is not set. Name of the Container App secret from which to pull the environment variable value.') + secretRef: string? + + @description('Optional. Required if secretRef is not set. Non-secret environment variable value.') + value: string? +} + +@export() +@description('Optional. Container resource requirements. Only used with custom containers.') +type sessionContainerResourceType = { + @description('Required. Required CPU in cores, e.g. 0.5.') + cpu: string + + @description('Required. Required memory, e.g. "1.25Gi".') + memory: string +} + +@export() +@description('Optional. Container registry credentials. Only used with custom containers.') +type sessionRegistryCredentialsType = { + @description('Optional. A Managed Identity to use to authenticate with Azure Container Registry. For user-assigned identities, use the full user-assigned identity Resource ID. For system-assigned identities, use "system".') + identity: string? + + @description('Optional. The name of the secret that contains the registry login password. Not used if identity is specified.') + passwordSecretRef: string? + + @description('Required. Container registry server.') + server: string + + @description('Required. Container registry username.') + username: string +} + +@export() +@description('Optional. Managed Identity settings for the session pool.') +type managedIdentitySettingType = { + @description('Required. The resource ID of a user-assigned managed identity that is assigned to the Session Pool, or "system" for system-assigned identity.') + identity: string + + @description('Required. Use to select the lifecycle stages of a Session Pool during which the Managed Identity should be available. Valid values: "All", "Init", "Main", "None".') + lifecycle: string +} diff --git a/avm/res/app/session-pool/main.json b/avm/res/app/session-pool/main.json new file mode 100644 index 0000000000..ceba343cb1 --- /dev/null +++ b/avm/res/app/session-pool/main.json @@ -0,0 +1,601 @@ +{ + "$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.31.92.45157", + "templateHash": "11243086501777108136" + }, + "name": "Container App Session Pool", + "description": "This module deploys a Container App Session Pool." + }, + "definitions": { + "sessionContainerType": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container start command." + } + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/sessionContainerEnvType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container environment variables." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. Container image tag." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Custom container name." + } + }, + "resources": { + "$ref": "#/definitions/sessionContainerResourceType", + "metadata": { + "description": "Required. Container resource requirements." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Optional. Custom container definition." + } + }, + "sessionContainerEnvType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Environment variable name." + } + }, + "secretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Required if value is not set. Name of the Container App secret from which to pull the environment variable value." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Required if secretRef is not set. Non-secret environment variable value." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Optional. Environment variable definition for a container. Only used with custom containers." + } + }, + "sessionContainerResourceType": { + "type": "object", + "properties": { + "cpu": { + "type": "string", + "metadata": { + "description": "Required. Required CPU in cores, e.g. 0.5." + } + }, + "memory": { + "type": "string", + "metadata": { + "description": "Required. Required memory, e.g. \"1.25Gi\"." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Optional. Container resource requirements. Only used with custom containers." + } + }, + "sessionRegistryCredentialsType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A Managed Identity to use to authenticate with Azure Container Registry. For user-assigned identities, use the full user-assigned identity Resource ID. For system-assigned identities, use \"system\"." + } + }, + "passwordSecretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the secret that contains the registry login password. Not used if identity is specified." + } + }, + "server": { + "type": "string", + "metadata": { + "description": "Required. Container registry server." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Required. Container registry username." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Optional. Container registry credentials. Only used with custom containers." + } + }, + "managedIdentitySettingType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a user-assigned managed identity that is assigned to the Session Pool, or \"system\" for system-assigned identity." + } + }, + "lifecycle": { + "type": "string", + "metadata": { + "description": "Required. Use to select the lifecycle stages of a Session Pool during which the Managed Identity should be available. Valid values: \"All\", \"Init\", \"Main\", \"None\"." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Optional. Managed Identity settings for the session pool." + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App Session Pool." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "containerType": { + "type": "string", + "allowedValues": [ + "PythonLTS", + "CustomContainer" + ], + "metadata": { + "description": "Required. The container type of the sessions." + } + }, + "containers": { + "type": "array", + "items": { + "$ref": "#/definitions/sessionContainerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom container definitions. Only required if containerType is CustomContainer." + } + }, + "targetIngressPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Required if containerType == 'CustomContainer'. Target port in containers for traffic from ingress. Only required if containerType is CustomContainer." + } + }, + "registryCredentials": { + "$ref": "#/definitions/sessionRegistryCredentialsType", + "nullable": true, + "metadata": { + "description": "Optional. Container registry credentials. Only required if containerType is CustomContainer and the container registry requires authentication." + } + }, + "cooldownPeriodInSeconds": { + "type": "int", + "defaultValue": 300, + "metadata": { + "description": "Optional. The cooldown period of a session in seconds." + } + }, + "maxConcurrentSessions": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The maximum count of sessions at the same time." + } + }, + "readySessionInstances": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The minimum count of ready session instances." + } + }, + "sessionNetworkStatus": { + "type": "string", + "defaultValue": "EgressDisabled", + "allowedValues": [ + "EgressEnabled", + "EgressDisabled" + ], + "metadata": { + "description": "Optional. Network status for the sessions. Defaults to EgressDisabled." + } + }, + "poolManagementType": { + "type": "string", + "defaultValue": "Dynamic", + "allowedValues": [ + "Dynamic", + "Manual" + ], + "metadata": { + "description": "Optional. The pool management type of the session pool. Defaults to Dynamic." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "managedIdentitySettings": { + "type": "array", + "items": { + "$ref": "#/definitions/managedIdentitySettingType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Settings for a Managed Identity that is assigned to the Session pool." + } + }, + "environmentId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the session pool's environment." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Automation Account resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), 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'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Azure ContainerApps Session Executor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fb8eba5-a2bb-4abe-b1c1-49dfad359bb0')]", + "Container Apps SessionPools Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7669afb-68b2-44b4-9c5f-6d2a47fddda0')]", + "Container Apps SessionPools Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'af61e8fc-2633-4b95-bed3-421ad6826515')]", + "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": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.app-sessionpool.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "#_moduleVersion_#.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "sessionPool": { + "type": "Microsoft.App/sessionPools", + "apiVersion": "2024-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "properties": { + "containerType": "[parameters('containerType')]", + "environmentId": "[parameters('environmentId')]", + "customContainerTemplate": "[if(equals(parameters('containerType'), 'CustomContainer'), createObject('containers', parameters('containers'), 'ingress', createObject('targetPort', parameters('targetIngressPort')), 'registryCredentials', parameters('registryCredentials')), null())]", + "dynamicPoolConfiguration": { + "cooldownPeriodInSeconds": "[parameters('cooldownPeriodInSeconds')]", + "executionType": "Timed" + }, + "managedIdentitySettings": "[parameters('managedIdentitySettings')]", + "scaleConfiguration": { + "maxConcurrentSessions": "[parameters('maxConcurrentSessions')]", + "readySessionInstances": "[parameters('readySessionInstances')]" + }, + "sessionNetworkConfiguration": { + "status": "[parameters('sessionNetworkStatus')]" + }, + "poolManagementType": "[parameters('poolManagementType')]" + }, + "tags": "[parameters('tags')]" + }, + "sessionPool_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.App/sessionPools/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "sessionPool" + ] + }, + "sessionPool_roleAssignments": { + "copy": { + "name": "sessionPool_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/sessionPools/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/sessionPools', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "sessionPool" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the session pool." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed session pool." + }, + "value": "[resourceId('Microsoft.App/sessionPools', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group in which the session pool was created." + }, + "value": "[resourceGroup().name]" + }, + "managementEndpoint": { + "type": "string", + "metadata": { + "description": "The management endpoint of the session pool." + }, + "value": "[reference('sessionPool').poolManagementEndpoint]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('sessionPool', '2024-10-02-preview', 'full'), 'identity'), 'principalId'), '')]" + } + } +} \ No newline at end of file diff --git a/avm/res/app/session-pool/tests/e2e/defaults/main.test.bicep b/avm/res/app/session-pool/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..3c9d108665 --- /dev/null +++ b/avm/res/app/session-pool/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,50 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.session-pool-${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 = 'aspmin' + +@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: 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' + location: resourceLocation + containerType: 'PythonLTS' + } + } +] diff --git a/avm/res/app/session-pool/tests/e2e/max/dependencies.bicep b/avm/res/app/session-pool/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..a73969ae10 --- /dev/null +++ b/avm/res/app/session-pool/tests/e2e/max/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to be created.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = { + name: managedIdentityName + location: location +} + +@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 diff --git a/avm/res/app/session-pool/tests/e2e/max/main.test.bicep b/avm/res/app/session-pool/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..1d5f073ab3 --- /dev/null +++ b/avm/res/app/session-pool/tests/e2e/max/main.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.session-pool-${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 = 'aspmax' + +@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: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' + } +} + +// ============== // +// 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' + location: resourceLocation + containerType: 'PythonLTS' + tags: { + resourceType: 'Session Pool' + } + cooldownPeriodInSeconds: 350 + maxConcurrentSessions: 6 + readySessionInstances: 1 + sessionNetworkStatus: 'EgressDisabled' + poolManagementType: 'Dynamic' + managedIdentitySettings: [ + { + identity: nestedDependencies.outputs.managedIdentityResourceId + lifecycle: 'Main' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Azure ContainerApps Session Executor' + principalType: 'ServicePrincipal' + } + ] + } + } +] diff --git a/avm/res/app/session-pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/session-pool/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..925c44814d --- /dev/null +++ b/avm/res/app/session-pool/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,54 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.session-pool-${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 = 'aspwaf' + +@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: 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' + location: resourceLocation + containerType: 'PythonLTS' + tags: { + resourceType: 'Session Pool' + } + sessionNetworkStatus: 'EgressDisabled' + } + } +] diff --git a/avm/res/app/session-pool/version.json b/avm/res/app/session-pool/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/app/session-pool/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 4ec73a017d092165cd4367dc533daf11602efda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= <rene@hezser.de> Date: Thu, 13 Feb 2025 09:14:56 +0100 Subject: [PATCH 26/53] fix: Accelerated Network `res/compute/gallery` (#4432) ## Description Fixes accelerated networking property for `res/compute/gallery` module, updates RP references and adds the new property allowUpdateImage. Closes #4374 references #4349 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.compute.gallery](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml/badge.svg)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] 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. - [x] 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 --- avm/res/compute/gallery/README.md | 59 +++++++++++++- avm/res/compute/gallery/application/README.md | 2 +- .../compute/gallery/application/main.bicep | 4 +- avm/res/compute/gallery/application/main.json | 10 +-- avm/res/compute/gallery/image/README.md | 27 ++++++- avm/res/compute/gallery/image/main.bicep | 35 ++++++--- avm/res/compute/gallery/image/main.json | 34 ++++++-- avm/res/compute/gallery/main.bicep | 10 ++- avm/res/compute/gallery/main.json | 77 +++++++++++++++---- .../gallery/tests/e2e/max/main.test.bicep | 8 +- avm/res/compute/gallery/version.json | 2 +- 11 files changed, 218 insertions(+), 50 deletions(-) diff --git a/avm/res/compute/gallery/README.md b/avm/res/compute/gallery/README.md index d6a31db48f..9427470e22 100644 --- a/avm/res/compute/gallery/README.md +++ b/avm/res/compute/gallery/README.md @@ -9,6 +9,7 @@ This module deploys an Azure Compute Gallery (formerly known as Shared Image Gal - [Parameters](#Parameters) - [Outputs](#Outputs) - [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) - [Data Collection](#Data-Collection) ## Resource Types @@ -17,9 +18,9 @@ This module deploys an Azure Compute Gallery (formerly known as Shared Image Gal | :-- | :-- | | `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.Compute/galleries` | [2023-07-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-07-03/galleries) | -| `Microsoft.Compute/galleries/applications` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/applications) | -| `Microsoft.Compute/galleries/images` | [2023-07-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-07-03/galleries/images) | +| `Microsoft.Compute/galleries` | [2024-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-03/galleries) | +| `Microsoft.Compute/galleries/applications` | [2024-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-03/galleries/applications) | +| `Microsoft.Compute/galleries/images` | [2024-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-03/galleries/images) | ## Usage examples @@ -144,6 +145,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { description: 'This is a test deployment.' images: [ { + allowUpdateImage: true architecture: 'x64' description: 'testDescription' endOfLife: '2033-01-01' @@ -169,6 +171,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { releaseNoteUri: 'https://testReleaseNoteUri.com' } { + allowUpdateImage: false hyperVGeneration: 'V2' identifier: { offer: 'WindowsServer' @@ -215,6 +218,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { } } { + diskControllerType: 'SCSI' hyperVGeneration: 'V2' identifier: { offer: '0001-com-ubuntu-minimal-focal' @@ -235,6 +239,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { } } { + diskControllerType: 'SCSI, NVMe' hyperVGeneration: 'V2' identifier: { offer: '0001-com-ubuntu-minimal-focal' @@ -377,6 +382,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { "images": { "value": [ { + "allowUpdateImage": true, "architecture": "x64", "description": "testDescription", "endOfLife": "2033-01-01", @@ -402,6 +408,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { "releaseNoteUri": "https://testReleaseNoteUri.com" }, { + "allowUpdateImage": false, "hyperVGeneration": "V2", "identifier": { "offer": "WindowsServer", @@ -448,6 +455,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { } }, { + "diskControllerType": "SCSI", "hyperVGeneration": "V2", "identifier": { "offer": "0001-com-ubuntu-minimal-focal", @@ -468,6 +476,7 @@ module gallery 'br/public:avm/res/compute/gallery:<version>' = { } }, { + "diskControllerType": "SCSI, NVMe", "hyperVGeneration": "V2", "identifier": { "offer": "0001-com-ubuntu-minimal-focal", @@ -610,6 +619,7 @@ param applications = [ param description = 'This is a test deployment.' param images = [ { + allowUpdateImage: true architecture: 'x64' description: 'testDescription' endOfLife: '2033-01-01' @@ -635,6 +645,7 @@ param images = [ releaseNoteUri: 'https://testReleaseNoteUri.com' } { + allowUpdateImage: false hyperVGeneration: 'V2' identifier: { offer: 'WindowsServer' @@ -681,6 +692,7 @@ param images = [ } } { + diskControllerType: 'SCSI' hyperVGeneration: 'V2' identifier: { offer: '0001-com-ubuntu-minimal-focal' @@ -701,6 +713,7 @@ param images = [ } } { + diskControllerType: 'SCSI, NVMe' hyperVGeneration: 'V2' identifier: { offer: '0001-com-ubuntu-minimal-focal' @@ -1296,8 +1309,10 @@ Images to create. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`allowUpdateImage`](#parameter-imagesallowupdateimage) | bool | Must be set to true if the gallery image features are being updated. | | [`architecture`](#parameter-imagesarchitecture) | string | The architecture of the image. Applicable to OS disks only. | | [`description`](#parameter-imagesdescription) | string | The description of this gallery image definition resource. This property is updatable. | +| [`diskControllerType`](#parameter-imagesdiskcontrollertype) | string | The disk controllers that an OS disk supports. | | [`endOfLife`](#parameter-imagesendoflife) | string | The end of life date of the gallery image definition. This property can be used for decommissioning purposes. This property is updatable. | | [`eula`](#parameter-imageseula) | string | The Eula agreement for the gallery image definition. | | [`excludedDiskTypes`](#parameter-imagesexcludeddisktypes) | array | Describes the disallowed disk types. | @@ -1382,6 +1397,13 @@ This property allows you to specify the type of the OS that is included in the d ] ``` +### Parameter: `images.allowUpdateImage` + +Must be set to true if the gallery image features are being updated. + +- Required: No +- Type: bool + ### Parameter: `images.architecture` The architecture of the image. Applicable to OS disks only. @@ -1403,6 +1425,21 @@ The description of this gallery image definition resource. This property is upda - Required: No - Type: string +### Parameter: `images.diskControllerType` + +The disk controllers that an OS disk supports. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'NVMe, SCSI' + 'SCSI' + 'SCSI, NVMe' + ] + ``` + ### Parameter: `images.endOfLife` The end of life date of the gallery image definition. This property can be used for decommissioning purposes. This property is updatable. @@ -1772,6 +1809,22 @@ This section gives you an overview of all local-referenced module files (i.e., o | :-- | :-- | | `br/public:avm/utl/types/avm-common-types:0.3.0` | Remote reference | +## Notes + +Currently it is not possible to redeploy the `image.diskControllerType` property with a value of `NVMe, SCSI`. The initial deployment is working, but other deployments will result in an error. + +```json +"details": [ + { + "code": "PropertyChangeNotAllowed", + "target": "DiskControllerTypes", + "message": "Changing property 'DiskControllerTypes' is not allowed." + } + ] +``` + +Once this bug has been resolved, the max test will be updated to deploy an image with the property value. + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/compute/gallery/application/README.md b/avm/res/compute/gallery/application/README.md index 9400c80cf4..80c590cb8a 100644 --- a/avm/res/compute/gallery/application/README.md +++ b/avm/res/compute/gallery/application/README.md @@ -15,7 +15,7 @@ This module deploys an Azure Compute Gallery Application. | 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.Compute/galleries/applications` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/applications) | +| `Microsoft.Compute/galleries/applications` | [2024-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-03/galleries/applications) | ## Parameters diff --git a/avm/res/compute/gallery/application/main.bicep b/avm/res/compute/gallery/application/main.bicep index 65a1418585..b8cddc8517 100644 --- a/avm/res/compute/gallery/application/main.bicep +++ b/avm/res/compute/gallery/application/main.bicep @@ -72,11 +72,11 @@ var formattedRoleAssignments = [ }) ] -resource gallery 'Microsoft.Compute/galleries@2022-03-03' existing = { +resource gallery 'Microsoft.Compute/galleries@2024-03-03' existing = { name: galleryName } -resource application 'Microsoft.Compute/galleries/applications@2022-03-03' = { +resource application 'Microsoft.Compute/galleries/applications@2024-03-03' = { name: name parent: gallery location: location diff --git a/avm/res/compute/gallery/application/main.json b/avm/res/compute/gallery/application/main.json index ca08bbc5b4..389026f8c7 100644 --- a/avm/res/compute/gallery/application/main.json +++ b/avm/res/compute/gallery/application/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7761447372947910331" + "version": "0.33.93.31351", + "templateHash": "8421640590183520512" }, "name": "Compute Galleries Applications", "description": "This module deploys an Azure Compute Gallery Application." @@ -281,12 +281,12 @@ "gallery": { "existing": true, "type": "Microsoft.Compute/galleries", - "apiVersion": "2022-03-03", + "apiVersion": "2024-03-03", "name": "[parameters('galleryName')]" }, "application": { "type": "Microsoft.Compute/galleries/applications", - "apiVersion": "2022-03-03", + "apiVersion": "2024-03-03", "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -350,7 +350,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('application', '2022-03-03', 'full').location]" + "value": "[reference('application', '2024-03-03', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/compute/gallery/image/README.md b/avm/res/compute/gallery/image/README.md index 0dbff55826..a5cf55dbb0 100644 --- a/avm/res/compute/gallery/image/README.md +++ b/avm/res/compute/gallery/image/README.md @@ -13,7 +13,7 @@ This module deploys an Azure Compute Gallery Image Definition. | 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.Compute/galleries/images` | [2023-07-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-07-03/galleries/images) | +| `Microsoft.Compute/galleries/images` | [2024-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-03/galleries/images) | ## Parameters @@ -36,9 +36,11 @@ This module deploys an Azure Compute Gallery Image Definition. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`allowUpdateImage`](#parameter-allowupdateimage) | bool | Must be set to true if the gallery image features are being updated. | | [`architecture`](#parameter-architecture) | string | The architecture of the image. Applicable to OS disks only. | | [`description`](#parameter-description) | string | The description of this gallery image definition resource. This property is updatable. | | [`disallowed`](#parameter-disallowed) | object | Describes the disallowed disk types. | +| [`diskControllerType`](#parameter-diskcontrollertype) | string | The disk controllers that an OS disk supports. | | [`endOfLifeDate`](#parameter-endoflifedate) | string | The end of life date of the gallery image definition. This property can be used for decommissioning purposes. This property is updatable. | | [`eula`](#parameter-eula) | string | The Eula agreement for the gallery image definition. | | [`hyperVGeneration`](#parameter-hypervgeneration) | string | The hypervisor generation of the Virtual Machine. If this value is not specified, then it is determined by the securityType parameter. If the securityType parameter is specified, then the value of hyperVGeneration will be V2, else V1. | @@ -132,6 +134,13 @@ The name of the parent Azure Shared Image Gallery. Required if the template is u - Required: Yes - Type: string +### Parameter: `allowUpdateImage` + +Must be set to true if the gallery image features are being updated. + +- Required: No +- Type: bool + ### Parameter: `architecture` The architecture of the image. Applicable to OS disks only. @@ -179,6 +188,21 @@ A list of disk types. ] ``` +### Parameter: `diskControllerType` + +The disk controllers that an OS disk supports. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'NVMe, SCSI' + 'SCSI' + 'SCSI, NVMe' + ] + ``` + ### Parameter: `endOfLifeDate` The end of life date of the gallery image definition. This property can be used for decommissioning purposes. This property is updatable. @@ -213,7 +237,6 @@ Specify if the image supports accelerated networking. - Required: No - Type: bool -- Default: `True` ### Parameter: `isHibernateSupported` diff --git a/avm/res/compute/gallery/image/main.bicep b/avm/res/compute/gallery/image/main.bicep index c09179fbd7..ae277e2cf0 100644 --- a/avm/res/compute/gallery/image/main.bicep +++ b/avm/res/compute/gallery/image/main.bicep @@ -47,11 +47,14 @@ param securityType ( | 'ConfidentialVMSupported')? @sys.description('Optional. Specify if the image supports accelerated networking.') -param isAcceleratedNetworkSupported bool = true +param isAcceleratedNetworkSupported bool? @sys.description('Optional. Specifiy if the image supports hibernation.') param isHibernateSupported bool? +@sys.description('Optional. Must be set to true if the gallery image features are being updated.') +param allowUpdateImage bool? + @sys.description('Optional. The architecture of the image. Applicable to OS disks only.') param architecture ('x64' | 'Arm64')? @@ -70,6 +73,9 @@ param eula string? @sys.description('Optional. The hypervisor generation of the Virtual Machine. If this value is not specified, then it is determined by the securityType parameter. If the securityType parameter is specified, then the value of hyperVGeneration will be V2, else V1.') param hyperVGeneration ('V1' | 'V2')? +@sys.description('Optional. The disk controllers that an OS disk supports.') +param diskControllerType ('SCSI' | 'SCSI, NVMe' | 'NVMe, SCSI')? + @sys.description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType @@ -113,16 +119,17 @@ var formattedRoleAssignments = [ }) ] -resource gallery 'Microsoft.Compute/galleries@2023-07-03' existing = { +resource gallery 'Microsoft.Compute/galleries@2024-03-03' existing = { name: galleryName } -resource image 'Microsoft.Compute/galleries/images@2023-07-03' = { +resource image 'Microsoft.Compute/galleries/images@2024-03-03' = { name: name parent: gallery location: location tags: tags properties: { + allowUpdateImage: allowUpdateImage != null ? allowUpdateImage : null architecture: architecture description: description disallowed: { @@ -131,12 +138,14 @@ resource image 'Microsoft.Compute/galleries/images@2023-07-03' = { endOfLifeDate: endOfLifeDate eula: eula features: union( - [ - { - name: 'IsAcceleratedNetworkSupported' - value: '${isAcceleratedNetworkSupported}' - } - ], + (isAcceleratedNetworkSupported != null // Accelerated network is not set by default and must not be set for unsupported skus + ? [ + { + name: 'IsAcceleratedNetworkSupported' + value: '${isAcceleratedNetworkSupported}' + } + ] + : []), (securityType != null && securityType != 'Standard' // Standard is the default and is not set ? [ { @@ -152,6 +161,14 @@ resource image 'Microsoft.Compute/galleries/images@2023-07-03' = { value: '${isHibernateSupported}' } ] + : []), + (diskControllerType != null + ? [ + { + name: 'DiskControllerTypes' + value: '${diskControllerType}' + } + ] : []) ) hyperVGeneration: hyperVGeneration ?? (!empty(securityType ?? '') ? 'V2' : 'V1') diff --git a/avm/res/compute/gallery/image/main.json b/avm/res/compute/gallery/image/main.json index 62d581baa5..647150763c 100644 --- a/avm/res/compute/gallery/image/main.json +++ b/avm/res/compute/gallery/image/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13858460997059168010" + "version": "0.33.93.31351", + "templateHash": "5191633407919649272" }, "name": "Compute Galleries Image Definitions", "description": "This module deploys an Azure Compute Gallery Image Definition." @@ -285,7 +285,7 @@ }, "isAcceleratedNetworkSupported": { "type": "bool", - "defaultValue": true, + "nullable": true, "metadata": { "description": "Optional. Specify if the image supports accelerated networking." } @@ -297,6 +297,13 @@ "description": "Optional. Specifiy if the image supports hibernation." } }, + "allowUpdateImage": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Must be set to true if the gallery image features are being updated." + } + }, "architecture": { "type": "string", "allowedValues": [ @@ -347,6 +354,18 @@ "description": "Optional. The hypervisor generation of the Virtual Machine. If this value is not specified, then it is determined by the securityType parameter. If the securityType parameter is specified, then the value of hyperVGeneration will be V2, else V1." } }, + "diskControllerType": { + "type": "string", + "allowedValues": [ + "NVMe, SCSI", + "SCSI", + "SCSI, NVMe" + ], + "nullable": true, + "metadata": { + "description": "Optional. The disk controllers that an OS disk supports." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -383,16 +402,17 @@ "gallery": { "existing": true, "type": "Microsoft.Compute/galleries", - "apiVersion": "2023-07-03", + "apiVersion": "2024-03-03", "name": "[parameters('galleryName')]" }, "image": { "type": "Microsoft.Compute/galleries/images", - "apiVersion": "2023-07-03", + "apiVersion": "2024-03-03", "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { + "allowUpdateImage": "[if(not(equals(parameters('allowUpdateImage'), null())), parameters('allowUpdateImage'), null())]", "architecture": "[parameters('architecture')]", "description": "[parameters('description')]", "disallowed": { @@ -400,7 +420,7 @@ }, "endOfLifeDate": "[parameters('endOfLifeDate')]", "eula": "[parameters('eula')]", - "features": "[union(createArray(createObject('name', 'IsAcceleratedNetworkSupported', 'value', format('{0}', parameters('isAcceleratedNetworkSupported')))), if(and(not(equals(parameters('securityType'), null())), not(equals(parameters('securityType'), 'Standard'))), createArray(createObject('name', 'SecurityType', 'value', format('{0}', parameters('securityType')))), createArray()), if(not(equals(parameters('isHibernateSupported'), null())), createArray(createObject('name', 'IsHibernateSupported', 'value', format('{0}', parameters('isHibernateSupported')))), createArray()))]", + "features": "[union(if(not(equals(parameters('isAcceleratedNetworkSupported'), null())), createArray(createObject('name', 'IsAcceleratedNetworkSupported', 'value', format('{0}', parameters('isAcceleratedNetworkSupported')))), createArray()), if(and(not(equals(parameters('securityType'), null())), not(equals(parameters('securityType'), 'Standard'))), createArray(createObject('name', 'SecurityType', 'value', format('{0}', parameters('securityType')))), createArray()), if(not(equals(parameters('isHibernateSupported'), null())), createArray(createObject('name', 'IsHibernateSupported', 'value', format('{0}', parameters('isHibernateSupported')))), createArray()), if(not(equals(parameters('diskControllerType'), null())), createArray(createObject('name', 'DiskControllerTypes', 'value', format('{0}', parameters('diskControllerType')))), createArray()))]", "hyperVGeneration": "[coalesce(parameters('hyperVGeneration'), if(not(empty(coalesce(parameters('securityType'), ''))), 'V2', 'V1'))]", "identifier": { "publisher": "[parameters('identifier').publisher]", @@ -468,7 +488,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('image', '2023-07-03', 'full').location]" + "value": "[reference('image', '2024-03-03', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/compute/gallery/main.bicep b/avm/res/compute/gallery/main.bicep index 4a85a01615..0902a56202 100644 --- a/avm/res/compute/gallery/main.bicep +++ b/avm/res/compute/gallery/main.bicep @@ -101,7 +101,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource gallery 'Microsoft.Compute/galleries@2023-07-03' = { +resource gallery 'Microsoft.Compute/galleries@2024-03-03' = { name: name location: location tags: tags @@ -168,6 +168,7 @@ module galleries_images 'image/main.bicep' = [ location: image.?location ?? location galleryName: gallery.name description: image.?description + allowUpdateImage: image.?allowUpdateImage osType: image.osType osState: image.osState identifier: image.identifier @@ -177,6 +178,7 @@ module galleries_images 'image/main.bicep' = [ securityType: image.?securityType isAcceleratedNetworkSupported: image.?isAcceleratedNetworkSupported isHibernateSupported: image.?isHibernateSupported + diskControllerType: image.?diskControllerType architecture: image.?architecture eula: image.?eula privacyStatementUri: image.?privacyStatementUri @@ -226,6 +228,9 @@ type imageType = { @sys.description('Optional. The description of this gallery image definition resource. This property is updatable.') description: string? + @sys.description('Optional. Must be set to true if the gallery image features are being updated.') + allowUpdateImage: bool? + @sys.description('Required. This property allows you to specify the type of the OS that is included in the disk when creating a VM from a managed image.') osType: ('Linux' | 'Windows') @@ -259,6 +264,9 @@ type imageType = { @sys.description('Optional. Specify if the image supports hibernation.') isHibernateSupported: bool? + @sys.description('Optional. The disk controllers that an OS disk supports.') + diskControllerType: ('SCSI' | 'SCSI, NVMe' | 'NVMe, SCSI')? + @sys.description('Optional. The architecture of the image. Applicable to OS disks only.') architecture: ('x64' | 'Arm64')? diff --git a/avm/res/compute/gallery/main.json b/avm/res/compute/gallery/main.json index 87eed5748c..cbcc15f39d 100644 --- a/avm/res/compute/gallery/main.json +++ b/avm/res/compute/gallery/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8716597573060065028" + "version": "0.33.93.31351", + "templateHash": "13276352345178978927" }, "name": "Azure Compute Galleries", "description": "This module deploys an Azure Compute Gallery (formerly known as Shared Image Gallery)." @@ -30,6 +30,13 @@ "description": "Optional. The description of this gallery image definition resource. This property is updatable." } }, + "allowUpdateImage": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Must be set to true if the gallery image features are being updated." + } + }, "osType": { "type": "string", "allowedValues": [ @@ -110,6 +117,18 @@ "description": "Optional. Specify if the image supports hibernation." } }, + "diskControllerType": { + "type": "string", + "allowedValues": [ + "NVMe, SCSI", + "SCSI", + "SCSI, NVMe" + ], + "nullable": true, + "metadata": { + "description": "Optional. The disk controllers that an OS disk supports." + } + }, "architecture": { "type": "string", "allowedValues": [ @@ -653,7 +672,7 @@ }, "gallery": { "type": "Microsoft.Compute/galleries", - "apiVersion": "2023-07-03", + "apiVersion": "2024-03-03", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -757,8 +776,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "7761447372947910331" + "version": "0.33.93.31351", + "templateHash": "8421640590183520512" }, "name": "Compute Galleries Applications", "description": "This module deploys an Azure Compute Gallery Application." @@ -1033,12 +1052,12 @@ "gallery": { "existing": true, "type": "Microsoft.Compute/galleries", - "apiVersion": "2022-03-03", + "apiVersion": "2024-03-03", "name": "[parameters('galleryName')]" }, "application": { "type": "Microsoft.Compute/galleries/applications", - "apiVersion": "2022-03-03", + "apiVersion": "2024-03-03", "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1102,7 +1121,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('application', '2022-03-03', 'full').location]" + "value": "[reference('application', '2024-03-03', 'full').location]" } } } @@ -1137,6 +1156,9 @@ "description": { "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'description')]" }, + "allowUpdateImage": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'allowUpdateImage')]" + }, "osType": { "value": "[coalesce(parameters('images'), createArray())[copyIndex()].osType]" }, @@ -1164,6 +1186,9 @@ "isHibernateSupported": { "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'isHibernateSupported')]" }, + "diskControllerType": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'diskControllerType')]" + }, "architecture": { "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'architecture')]" }, @@ -1201,8 +1226,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13858460997059168010" + "version": "0.33.93.31351", + "templateHash": "5191633407919649272" }, "name": "Compute Galleries Image Definitions", "description": "This module deploys an Azure Compute Gallery Image Definition." @@ -1481,7 +1506,7 @@ }, "isAcceleratedNetworkSupported": { "type": "bool", - "defaultValue": true, + "nullable": true, "metadata": { "description": "Optional. Specify if the image supports accelerated networking." } @@ -1493,6 +1518,13 @@ "description": "Optional. Specifiy if the image supports hibernation." } }, + "allowUpdateImage": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Must be set to true if the gallery image features are being updated." + } + }, "architecture": { "type": "string", "allowedValues": [ @@ -1543,6 +1575,18 @@ "description": "Optional. The hypervisor generation of the Virtual Machine. If this value is not specified, then it is determined by the securityType parameter. If the securityType parameter is specified, then the value of hyperVGeneration will be V2, else V1." } }, + "diskControllerType": { + "type": "string", + "allowedValues": [ + "NVMe, SCSI", + "SCSI", + "SCSI, NVMe" + ], + "nullable": true, + "metadata": { + "description": "Optional. The disk controllers that an OS disk supports." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -1579,16 +1623,17 @@ "gallery": { "existing": true, "type": "Microsoft.Compute/galleries", - "apiVersion": "2023-07-03", + "apiVersion": "2024-03-03", "name": "[parameters('galleryName')]" }, "image": { "type": "Microsoft.Compute/galleries/images", - "apiVersion": "2023-07-03", + "apiVersion": "2024-03-03", "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { + "allowUpdateImage": "[if(not(equals(parameters('allowUpdateImage'), null())), parameters('allowUpdateImage'), null())]", "architecture": "[parameters('architecture')]", "description": "[parameters('description')]", "disallowed": { @@ -1596,7 +1641,7 @@ }, "endOfLifeDate": "[parameters('endOfLifeDate')]", "eula": "[parameters('eula')]", - "features": "[union(createArray(createObject('name', 'IsAcceleratedNetworkSupported', 'value', format('{0}', parameters('isAcceleratedNetworkSupported')))), if(and(not(equals(parameters('securityType'), null())), not(equals(parameters('securityType'), 'Standard'))), createArray(createObject('name', 'SecurityType', 'value', format('{0}', parameters('securityType')))), createArray()), if(not(equals(parameters('isHibernateSupported'), null())), createArray(createObject('name', 'IsHibernateSupported', 'value', format('{0}', parameters('isHibernateSupported')))), createArray()))]", + "features": "[union(if(not(equals(parameters('isAcceleratedNetworkSupported'), null())), createArray(createObject('name', 'IsAcceleratedNetworkSupported', 'value', format('{0}', parameters('isAcceleratedNetworkSupported')))), createArray()), if(and(not(equals(parameters('securityType'), null())), not(equals(parameters('securityType'), 'Standard'))), createArray(createObject('name', 'SecurityType', 'value', format('{0}', parameters('securityType')))), createArray()), if(not(equals(parameters('isHibernateSupported'), null())), createArray(createObject('name', 'IsHibernateSupported', 'value', format('{0}', parameters('isHibernateSupported')))), createArray()), if(not(equals(parameters('diskControllerType'), null())), createArray(createObject('name', 'DiskControllerTypes', 'value', format('{0}', parameters('diskControllerType')))), createArray()))]", "hyperVGeneration": "[coalesce(parameters('hyperVGeneration'), if(not(empty(coalesce(parameters('securityType'), ''))), 'V2', 'V1'))]", "identifier": { "publisher": "[parameters('identifier').publisher]", @@ -1664,7 +1709,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('image', '2023-07-03', 'full').location]" + "value": "[reference('image', '2024-03-03', 'full').location]" } } } @@ -1701,7 +1746,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('gallery', '2023-07-03', 'full').location]" + "value": "[reference('gallery', '2024-03-03', 'full').location]" }, "imageResourceIds": { "type": "array", diff --git a/avm/res/compute/gallery/tests/e2e/max/main.test.bicep b/avm/res/compute/gallery/tests/e2e/max/main.test.bicep index bca3fb8249..c301dea7da 100644 --- a/avm/res/compute/gallery/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/gallery/tests/e2e/max/main.test.bicep @@ -91,6 +91,7 @@ module testDeployment '../../../main.bicep' = [ images: [ { name: '${namePrefix}-az-imgd-ws-001' + allowUpdateImage: true hyperVGeneration: 'V1' identifier: { publisher: 'MicrosoftWindowsServer' @@ -114,6 +115,7 @@ module testDeployment '../../../main.bicep' = [ } { name: '${namePrefix}-az-imgd-ws-002' + allowUpdateImage: false hyperVGeneration: 'V2' identifier: { publisher: 'MicrosoftWindowsServer' @@ -178,6 +180,7 @@ module testDeployment '../../../main.bicep' = [ max: 4 } isAcceleratedNetworkSupported: false + diskControllerType: 'SCSI' } { name: '${namePrefix}-az-imgd-us-005' @@ -198,6 +201,7 @@ module testDeployment '../../../main.bicep' = [ max: 4 } isAcceleratedNetworkSupported: true + diskControllerType: 'SCSI, NVMe' } { name: '${namePrefix}-az-imgd-us-006' @@ -232,6 +236,7 @@ module testDeployment '../../../main.bicep' = [ } releaseNoteUri: 'https://testReleaseNoteUri.com' isAcceleratedNetworkSupported: false + // diskControllerType: 'NVMe, SCSI' // --> needs to remain commented, as there is a bug setting the value starting with 'NVMe' again, which prevents the idem test to pass } ] roleAssignments: [ @@ -262,8 +267,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/compute/gallery/version.json b/avm/res/compute/gallery/version.json index 9a9a06e897..6b6be93891 100644 --- a/avm/res/compute/gallery/version.json +++ b/avm/res/compute/gallery/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] From f4894e0e5003e8f31b6347c730cdcdd01880e341 Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:47:15 +0200 Subject: [PATCH 27/53] fix: sub-vending, adding CI secret (#4442) ## Description Adding a custom CI secret to the sub-vending module ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.ptn.lz.sub-vending](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml/badge.svg?branch=avm-ptn-vending-add-secret)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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. - [X] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/ptn/lz/sub-vending/main.json | 60 +++++++++---------- .../tests/e2e/defaults/main.test.bicep | 6 +- .../tests/e2e/hub-spoke/main.test.bicep | 6 +- .../tests/e2e/rbac-condition/main.test.bicep | 6 +- .../tests/e2e/vwan-spoke/main.test.bicep | 6 +- avm/ptn/network/hub-networking/main.json | 16 ++--- 6 files changed, 50 insertions(+), 50 deletions(-) diff --git a/avm/ptn/lz/sub-vending/main.json b/avm/ptn/lz/sub-vending/main.json index 2421387f0d..649e69f153 100644 --- a/avm/ptn/lz/sub-vending/main.json +++ b/avm/ptn/lz/sub-vending/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "16856855794906870670" + "version": "0.33.93.31351", + "templateHash": "14919277706976174104" }, "name": "Sub-vending", "description": "This module deploys a subscription to accelerate deployment of landing zones. For more information on how to use it, please visit this [Wiki](https://github.com/Azure/bicep-lz-vending/wiki).", @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2801471703151139948" + "version": "0.33.93.31351", + "templateHash": "8425865084067531624" } }, "parameters": { @@ -880,8 +880,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "250791371811352682" + "version": "0.33.93.31351", + "templateHash": "11018695082972643897" }, "name": "`/subResourcesWrapper/deploy.bicep` Parameters", "description": "This module is used by the [`bicep-lz-vending`](https://aka.ms/sub-vending/bicep) module to help orchestrate the deployment", @@ -1588,8 +1588,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "12834168093418139358" + "version": "0.33.93.31351", + "templateHash": "8452628568304993719" } }, "parameters": { @@ -1649,8 +1649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15508893729756562690" + "version": "0.33.93.31351", + "templateHash": "11019372772177629958" } }, "parameters": { @@ -1709,8 +1709,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2541836993831686925" + "version": "0.33.93.31351", + "templateHash": "8397259050503224920" } }, "parameters": { @@ -1765,8 +1765,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "7264500549088500335" + "version": "0.33.93.31351", + "templateHash": "7623404265819505597" } }, "parameters": { @@ -1843,8 +1843,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9459644647794329484" + "version": "0.33.93.31351", + "templateHash": "8957892045766331539" } }, "parameters": { @@ -1898,8 +1898,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8593973730489733307" + "version": "0.33.93.31351", + "templateHash": "14513856367602857749" } }, "parameters": { @@ -2511,8 +2511,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15508893729756562690" + "version": "0.33.93.31351", + "templateHash": "11019372772177629958" } }, "parameters": { @@ -2571,8 +2571,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2541836993831686925" + "version": "0.33.93.31351", + "templateHash": "8397259050503224920" } }, "parameters": { @@ -2627,8 +2627,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "7264500549088500335" + "version": "0.33.93.31351", + "templateHash": "7623404265819505597" } }, "parameters": { @@ -2705,8 +2705,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9459644647794329484" + "version": "0.33.93.31351", + "templateHash": "8957892045766331539" } }, "parameters": { @@ -2760,8 +2760,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8593973730489733307" + "version": "0.33.93.31351", + "templateHash": "14513856367602857749" } }, "parameters": { @@ -4454,8 +4454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4537798139153123030" + "version": "0.33.93.31351", + "templateHash": "12390675321128699904" } }, "parameters": { diff --git a/avm/ptn/lz/sub-vending/tests/e2e/defaults/main.test.bicep b/avm/ptn/lz/sub-vending/tests/e2e/defaults/main.test.bicep index 2a0aba1b3b..ffc3e62a24 100644 --- a/avm/ptn/lz/sub-vending/tests/e2e/defaults/main.test.bicep +++ b/avm/ptn/lz/sub-vending/tests/e2e/defaults/main.test.bicep @@ -6,9 +6,9 @@ targetScope = 'managementGroup' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location -// This parameter needs to be updated with the billing account and the enrollment account of your enviornment. -@description('Optional. The subscription billing scope.') -param subscriptionBillingScope string = 'providers/Microsoft.Billing/billingAccounts/7690848/enrollmentAccounts/350580' +@description('Required. The scope of the subscription billing. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-SubscriptionBillingScope\'.') +@secure() +param subscriptionBillingScope string = '' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/ptn/lz/sub-vending/tests/e2e/hub-spoke/main.test.bicep b/avm/ptn/lz/sub-vending/tests/e2e/hub-spoke/main.test.bicep index e9a70825bb..e817b70b37 100644 --- a/avm/ptn/lz/sub-vending/tests/e2e/hub-spoke/main.test.bicep +++ b/avm/ptn/lz/sub-vending/tests/e2e/hub-spoke/main.test.bicep @@ -6,9 +6,9 @@ targetScope = 'managementGroup' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location -// This parameter needs to be updated with the billing account and the enrollment account of your enviornment. -@description('Optional. The subscription billing scope.') -param subscriptionBillingScope string = 'providers/Microsoft.Billing/billingAccounts/7690848/enrollmentAccounts/350580' +@description('Required. The scope of the subscription billing. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-SubscriptionBillingScope\'.') +@secure() +param subscriptionBillingScope string = '' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/ptn/lz/sub-vending/tests/e2e/rbac-condition/main.test.bicep b/avm/ptn/lz/sub-vending/tests/e2e/rbac-condition/main.test.bicep index c69578e2c9..8c897dcee0 100644 --- a/avm/ptn/lz/sub-vending/tests/e2e/rbac-condition/main.test.bicep +++ b/avm/ptn/lz/sub-vending/tests/e2e/rbac-condition/main.test.bicep @@ -6,9 +6,9 @@ targetScope = 'managementGroup' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location -// This parameter needs to be updated with the billing account and the enrollment account of your enviornment. -@description('Optional. The subscription billing scope.') -param subscriptionBillingScope string = 'providers/Microsoft.Billing/billingAccounts/7690848/enrollmentAccounts/350580' +@description('Required. The scope of the subscription billing. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-SubscriptionBillingScope\'.') +@secure() +param subscriptionBillingScope string = '' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/ptn/lz/sub-vending/tests/e2e/vwan-spoke/main.test.bicep b/avm/ptn/lz/sub-vending/tests/e2e/vwan-spoke/main.test.bicep index f4ebc8730c..48449af2f6 100644 --- a/avm/ptn/lz/sub-vending/tests/e2e/vwan-spoke/main.test.bicep +++ b/avm/ptn/lz/sub-vending/tests/e2e/vwan-spoke/main.test.bicep @@ -6,9 +6,9 @@ targetScope = 'managementGroup' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location -// This parameter needs to be updated with the billing account and the enrollment account of your enviornment. -@description('Optional. The subscription billing scope.') -param subscriptionBillingScope string = 'providers/Microsoft.Billing/billingAccounts/7690848/enrollmentAccounts/350580' +@description('Required. The scope of the subscription billing. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-SubscriptionBillingScope\'.') +@secure() +param subscriptionBillingScope string = '' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/ptn/network/hub-networking/main.json b/avm/ptn/network/hub-networking/main.json index f3c3c12f2a..124c1e8233 100644 --- a/avm/ptn/network/hub-networking/main.json +++ b/avm/ptn/network/hub-networking/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17389954898374222994" + "version": "0.33.93.31351", + "templateHash": "10309836000326275731" }, "name": "Hub Networking", "description": "This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing." @@ -2340,8 +2340,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13800865708253602673" + "version": "0.33.93.31351", + "templateHash": "6971888757750761810" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network." @@ -6467,8 +6467,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "17332298613317998164" + "version": "0.33.93.31351", + "templateHash": "4695558034353162860" }, "name": "Existing Virtual Network Subnets", "description": "This module retrieves an existing Virtual Network Subnet." @@ -6549,8 +6549,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13712864569972623739" + "version": "0.33.93.31351", + "templateHash": "3980815084200627301" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet." From 2cbae4e5632b3e63f3aa617ceab358818d7ddc5a Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Thu, 13 Feb 2025 18:46:03 +0200 Subject: [PATCH 28/53] fix: enhancements to hub-spoke module (#4443) ## Description Closes #4433 - Added options to specify bastion and route table names - Added values for bastion and firewall Skus in UDT - Added locks to route table - Updated the necessary tests ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.ptn.network.hub-networking](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml/badge.svg?branch=hub-spoke-enhancements)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] 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`. - [X] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/ptn/network/hub-networking/README.md | 69 ++++++++++++++++++- avm/ptn/network/hub-networking/main.bicep | 25 +++++-- avm/ptn/network/hub-networking/main.json | 56 +++++++++++++-- .../tests/e2e/max/main.test.bicep | 6 ++ 4 files changed, 146 insertions(+), 10 deletions(-) diff --git a/avm/ptn/network/hub-networking/README.md b/avm/ptn/network/hub-networking/README.md index b20fa6928b..9151a55c9b 100644 --- a/avm/ptn/network/hub-networking/README.md +++ b/avm/ptn/network/hub-networking/README.md @@ -118,8 +118,14 @@ module hubNetworking 'br/public:avm/ptn/network/hub-networking:<version>' = { name: 'hub1-waf-pip' } threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] } bastionHost: { + bastionHostName: 'bastion-hub1' disableCopyPaste: true enableFileCopy: false enableIpConnect: false @@ -298,9 +304,15 @@ module hubNetworking 'br/public:avm/ptn/network/hub-networking:<version>' = { "publicIPAddressObject": { "name": "hub1-waf-pip" }, - "threatIntelMode": "Alert" + "threatIntelMode": "Alert", + "zones": [ + 1, + 2, + 3 + ] }, "bastionHost": { + "bastionHostName": "bastion-hub1", "disableCopyPaste": true, "enableFileCopy": false, "enableIpConnect": false, @@ -480,8 +492,14 @@ param hubVirtualNetworks = { name: 'hub1-waf-pip' } threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] } bastionHost: { + bastionHostName: 'bastion-hub1' disableCopyPaste: true enableFileCopy: false enableIpConnect: false @@ -1255,6 +1273,7 @@ The hub virtual networks to create. | [`peeringSettings`](#parameter-hubvirtualnetworks>any_other_property<peeringsettings) | array | The peerings of the virtual network. | | [`roleAssignments`](#parameter-hubvirtualnetworks>any_other_property<roleassignments) | array | The role assignments to create. | | [`routes`](#parameter-hubvirtualnetworks>any_other_property<routes) | array | Routes to add to the virtual network route table. | +| [`routeTableName`](#parameter-hubvirtualnetworks>any_other_property<routetablename) | string | The name of the route table. | | [`subnets`](#parameter-hubvirtualnetworks>any_other_property<subnets) | array | The subnets of the virtual network. | | [`tags`](#parameter-hubvirtualnetworks>any_other_property<tags) | object | The tags of the virtual network. | | [`vnetEncryption`](#parameter-hubvirtualnetworks>any_other_property<vnetencryption) | bool | Enable/Disable VNet encryption. | @@ -1280,6 +1299,7 @@ The Azure Firewall config. | :-- | :-- | :-- | | [`additionalPublicIpConfigurations`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsadditionalpublicipconfigurations) | array | Additional public IP configurations. | | [`applicationRuleCollections`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsapplicationrulecollections) | array | Application rule collections. | +| [`azureFirewallName`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsazurefirewallname) | string | The name of the Azure Firewall. | | [`azureSkuTier`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsazureskutier) | string | Azure Firewall SKU. | | [`diagnosticSettings`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsdiagnosticsettings) | array | Diagnostic settings. | | [`enableTelemetry`](#parameter-hubvirtualnetworks>any_other_property<azurefirewallsettingsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | @@ -1313,12 +1333,27 @@ Application rule collections. - Required: No - Type: array +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.azureFirewallName` + +The name of the Azure Firewall. + +- Required: No +- Type: string + ### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.azureSkuTier` Azure Firewall SKU. - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'Basic' + 'Premium' + 'Standard' + ] + ``` ### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings` @@ -1708,13 +1743,22 @@ The Azure Bastion config. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`bastionHostName`](#parameter-hubvirtualnetworks>any_other_property<bastionhostbastionhostname) | string | The name of the bastion host. | | [`disableCopyPaste`](#parameter-hubvirtualnetworks>any_other_property<bastionhostdisablecopypaste) | bool | Enable/Disable copy/paste functionality. | | [`enableFileCopy`](#parameter-hubvirtualnetworks>any_other_property<bastionhostenablefilecopy) | bool | Enable/Disable file copy functionality. | | [`enableIpConnect`](#parameter-hubvirtualnetworks>any_other_property<bastionhostenableipconnect) | bool | Enable/Disable IP connect functionality. | +| [`enableKerberos`](#parameter-hubvirtualnetworks>any_other_property<bastionhostenablekerberos) | bool | Enable/Disable Kerberos authentication. | | [`enableShareableLink`](#parameter-hubvirtualnetworks>any_other_property<bastionhostenableshareablelink) | bool | Enable/Disable shareable link functionality. | | [`scaleUnits`](#parameter-hubvirtualnetworks>any_other_property<bastionhostscaleunits) | int | The number of scale units for the Bastion host. Defaults to 4. | | [`skuName`](#parameter-hubvirtualnetworks>any_other_property<bastionhostskuname) | string | The SKU name of the Bastion host. Defaults to Standard. | +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.bastionHostName` + +The name of the bastion host. + +- Required: No +- Type: string + ### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.disableCopyPaste` Enable/Disable copy/paste functionality. @@ -1736,6 +1780,13 @@ Enable/Disable IP connect functionality. - Required: No - Type: bool +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.enableKerberos` + +Enable/Disable Kerberos authentication. + +- Required: No +- Type: bool + ### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.enableShareableLink` Enable/Disable shareable link functionality. @@ -1756,6 +1807,15 @@ The SKU name of the Bastion host. Defaults to Standard. - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'Basic' + 'Developer' + 'Premium' + 'Standard' + ] + ``` ### Parameter: `hubVirtualNetworks.>Any_other_property<.ddosProtectionPlanResourceId` @@ -2151,6 +2211,13 @@ Routes to add to the virtual network route table. - Required: No - Type: array +### Parameter: `hubVirtualNetworks.>Any_other_property<.routeTableName` + +The name of the route table. + +- Required: No +- Type: string + ### Parameter: `hubVirtualNetworks.>Any_other_property<.subnets` The subnets of the virtual network. diff --git a/avm/ptn/network/hub-networking/main.bicep b/avm/ptn/network/hub-networking/main.bicep index b734027146..33573075b5 100644 --- a/avm/ptn/network/hub-networking/main.bicep +++ b/avm/ptn/network/hub-networking/main.bicep @@ -111,13 +111,14 @@ module hubRouteTable 'br/public:avm/res/network/route-table:0.4.0' = [ for (hub, index) in items(hubVirtualNetworks ?? {}): { name: '${uniqueString(deployment().name, location)}-${hub.key}-nrt' params: { - name: hub.key + name: hub.value.?routeTableName ?? hub.key location: hub.value.?location ?? location disableBgpRoutePropagation: true enableTelemetry: hub.value.?enableTelemetry ?? true roleAssignments: hub.value.?roleAssignments ?? [] routes: hub.value.?routes ?? [] tags: hub.value.?tags ?? {} + lock: hub.value.?lock ?? {} } dependsOn: hubVirtualNetwork } @@ -144,7 +145,7 @@ module hubBastion 'br/public:avm/res/network/bastion-host:0.4.0' = [ name: '${uniqueString(deployment().name, location)}-${hub.key}-nbh' params: { // Required parameters - name: hub.key + name: hub.value.?bastionHost.?bastionHostName ?? hub.key virtualNetworkResourceId: hubVirtualNetwork[index].outputs.resourceId // Non-required parameters diagnosticSettings: hub.value.?diagnosticSettings ?? [] @@ -158,6 +159,8 @@ module hubBastion 'br/public:avm/res/network/bastion-host:0.4.0' = [ scaleUnits: hub.value.?bastionHost.?scaleUnits ?? 4 skuName: hub.value.?bastionHost.?skuName ?? 'Standard' tags: hub.value.?tags ?? {} + lock: hub.value.?lock ?? {} + enableKerberos: hub.value.?bastionHost.?enableKerberos ?? false } dependsOn: hubVirtualNetwork } @@ -170,7 +173,7 @@ module hubAzureFirewall 'br/public:avm/res/network/azure-firewall:0.5.1' = [ name: '${uniqueString(deployment().name, location)}-${hub.key}-naf' params: { // Required parameters - name: hub.key + name: hub.value.?azureFirewallSettings.?azureFirewallName ?? hub.key // Conditional parameters hubIPAddresses: hub.value.?azureFirewallSettings.?hubIpAddresses ?? {} virtualHubId: hub.value.?azureFirewallSettings.?virtualHub ?? '' @@ -383,11 +386,17 @@ type hubVirtualNetworkType = { @description('Optional. Enable/Disable shareable link functionality.') enableShareableLink: bool? + @description('Optional. Enable/Disable Kerberos authentication.') + enableKerberos: bool? + @description('Optional. The number of scale units for the Bastion host. Defaults to 4.') scaleUnits: int? @description('Optional. The SKU name of the Bastion host. Defaults to Standard.') - skuName: string? + skuName: 'Basic' | 'Developer' | 'Premium' | 'Standard'? + + @description('Optional. The name of the bastion host.') + bastionHostName: string? }? @description('Optional. Enable/Disable usage telemetry for module.') @@ -429,6 +438,9 @@ type hubVirtualNetworkType = { @description('Optional. Routes to add to the virtual network route table.') routes: array? + @description('Optional. The name of the route table.') + routeTableName: string? + @description('Optional. The subnets of the virtual network.') subnets: array? @@ -461,6 +473,9 @@ type peeringSettingsType = { }[]? type azureFirewallType = { + @description('Optional. The name of the Azure Firewall.') + azureFirewallName: string? + @description('Optional. Hub IP addresses.') hubIpAddresses: object? @@ -474,7 +489,7 @@ type azureFirewallType = { applicationRuleCollections: array? @description('Optional. Azure Firewall SKU.') - azureSkuTier: string? + azureSkuTier: 'Basic' | 'Standard' | 'Premium'? @description('Optional. Diagnostic settings.') diagnosticSettings: diagnosticSettingType? diff --git a/avm/ptn/network/hub-networking/main.json b/avm/ptn/network/hub-networking/main.json index 124c1e8233..0c7284103c 100644 --- a/avm/ptn/network/hub-networking/main.json +++ b/avm/ptn/network/hub-networking/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "10309836000326275731" + "templateHash": "13578022486694565821" }, "name": "Hub Networking", "description": "This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing." @@ -280,6 +280,13 @@ "description": "Optional. Enable/Disable shareable link functionality." } }, + "enableKerberos": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable Kerberos authentication." + } + }, "scaleUnits": { "type": "int", "nullable": true, @@ -289,10 +296,23 @@ }, "skuName": { "type": "string", + "allowedValues": [ + "Basic", + "Developer", + "Premium", + "Standard" + ], "nullable": true, "metadata": { "description": "Optional. The SKU name of the Bastion host. Defaults to Standard." } + }, + "bastionHostName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the bastion host." + } } }, "nullable": true, @@ -391,6 +411,13 @@ "description": "Optional. Routes to add to the virtual network route table." } }, + "routeTableName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the route table." + } + }, "subnets": { "type": "array", "nullable": true, @@ -473,6 +500,13 @@ "azureFirewallType": { "type": "object", "properties": { + "azureFirewallName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Azure Firewall." + } + }, "hubIpAddresses": { "type": "object", "nullable": true, @@ -503,6 +537,11 @@ }, "azureSkuTier": { "type": "string", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], "nullable": true, "metadata": { "description": "Optional. Azure Firewall SKU." @@ -2406,7 +2445,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'routeTableName'), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]" }, "location": { "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'location'), parameters('location'))]" @@ -2425,6 +2464,9 @@ }, "tags": { "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + }, + "lock": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'lock'), createObject())]" } }, "template": { @@ -2790,7 +2832,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'bastionHostName'), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]" }, "virtualNetworkResourceId": { "value": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.resourceId.value]" @@ -2827,6 +2869,12 @@ }, "tags": { "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + }, + "lock": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'lock'), createObject())]" + }, + "enableKerberos": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'enableKerberos'), false())]" } }, "template": { @@ -4010,7 +4058,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'azureFirewallName'), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]" }, "hubIPAddresses": { "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'hubIpAddresses'), createObject())]" diff --git a/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep b/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep index 4a21691f16..f03c4132ff 100644 --- a/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep +++ b/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep @@ -71,8 +71,14 @@ module testDeployment '../../../main.bicep' = [ name: 'hub1-waf-pip' } threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] } bastionHost: { + bastionHostName: 'bastion-hub1' disableCopyPaste: true enableFileCopy: false enableIpConnect: false From 6b5d76162b80e84aed3e1fac0386f03605448b53 Mon Sep 17 00:00:00 2001 From: hundredacres <hundredacres.rm@yahoo.com> Date: Thu, 13 Feb 2025 09:16:25 -0800 Subject: [PATCH 29/53] feat: Allowing shardCount to be nullified (#4437) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Allow shardCount to be nullified to disable default clustering behavior. Below is the behavior of AVM before implementing the change to shardCount. <img width="1148" alt="Screenshot 2025-02-12 at 11 29 37 AM" src="https://github.com/user-attachments/assets/6889136c-4473-4de3-9352-f763234f6430" /> After implementing the fix, the Premium cache is not clustered. <img width="1151" alt="Screenshot 2025-02-12 at 11 28 56 AM" src="https://github.com/user-attachments/assets/519f9de7-ece9-4d6e-ac39-4fbc551263dd" /> Fixes #4380 Closes #4380 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.cache.redis](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=feat%2Fissues%2F4380)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting 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`: - [X] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/res/cache/redis/README.md | 1 - avm/res/cache/redis/linked-servers/main.json | 4 ++-- avm/res/cache/redis/main.bicep | 2 +- avm/res/cache/redis/main.json | 14 +++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index 49297597e7..a47ace669a 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -2183,7 +2183,6 @@ The number of shards to be created on a Premium Cluster Cache. - Required: No - Type: int -- Default: `1` - MinValue: 1 ### Parameter: `skuName` diff --git a/avm/res/cache/redis/linked-servers/main.json b/avm/res/cache/redis/linked-servers/main.json index 170402a3d5..5c70c19270 100644 --- a/avm/res/cache/redis/linked-servers/main.json +++ b/avm/res/cache/redis/linked-servers/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "11294861621866290910" + "version": "0.33.93.31351", + "templateHash": "322534394383651316" }, "name": "Redis Cache Linked Servers", "description": "This module connects a primary and secondary Redis Cache together for geo-replication." diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index 461bab33b7..6494bafab4 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -64,7 +64,7 @@ param replicasPerPrimary int = 3 @minValue(1) @description('Optional. The number of shards to be created on a Premium Cluster Cache.') -param shardCount int = 1 +param shardCount int? @allowed([ 0 diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index 10bffbc878..d63179ebfc 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "1242857667100916577" + "version": "0.33.93.31351", + "templateHash": "2636464673565813214" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache." @@ -842,7 +842,7 @@ }, "shardCount": { "type": "int", - "defaultValue": 1, + "nullable": true, "minValue": 1, "metadata": { "description": "Optional. The number of shards to be created on a Premium Cluster Cache." @@ -1947,8 +1947,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "11294861621866290910" + "version": "0.33.93.31351", + "templateHash": "322534394383651316" }, "name": "Redis Cache Linked Servers", "description": "This module connects a primary and secondary Redis Cache together for geo-replication." @@ -2073,8 +2073,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "14045530027687796477" + "version": "0.33.93.31351", + "templateHash": "8063348652715653257" } }, "definitions": { From 89d67dda6fcc6b5242295857a8531f9d4b1bb823 Mon Sep 17 00:00:00 2001 From: Nicky Griffin <153103937+31Nick@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:01:07 +0000 Subject: [PATCH 30/53] feat: New module `avm/ptn/mgmt-groups/subscription-placement` (#3904) ## Description Adding the sub-placement pattern module Closes https://github.com/Azure/Azure-Verified-Modules/issues/1585#issuecomment-2516149681 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.ptn.mgmt-groups.subscription-placement](https://github.com/oZakari/bicep-registry-modules/actions/workflows/avm.ptn.mgmt-groups.subscription-placement.yml/badge.svg?branch=sub-placement-development)](https://github.com/oZakari/bicep-registry-modules/actions/workflows/avm.ptn.mgmt-groups.subscription-placement.yml)| ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 - [ ] @avm-core-team-technical-bicep TODO: Add custom secret 'subscriptionBillingScope', 'rootManagementGroupId' ; to AVM CI ## 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --------- Co-authored-by: Zach Trocinski <ztrocinski@outlook.com> Co-authored-by: Zach Trocinski <30884663+oZakari@users.noreply.github.com> Co-authored-by: Alexander Sehr <ASehr@hotmail.de> --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...ptn.mgmt-groups.subscription-placement.yml | 84 +++++++++ .../subscription-placement/README.md | 170 ++++++++++++++++++ .../subscription-placement/main.bicep | 67 +++++++ .../subscription-placement/main.json | 168 +++++++++++++++++ .../modules/helper.bicep | 19 ++ .../tests/e2e/defaults/dependencies.bicep | 50 ++++++ .../tests/e2e/defaults/main.test.bicep | 52 ++++++ .../subscription-placement/version.json | 7 + 10 files changed, 619 insertions(+) create mode 100644 .github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml create mode 100644 avm/ptn/mgmt-groups/subscription-placement/README.md create mode 100644 avm/ptn/mgmt-groups/subscription-placement/main.bicep create mode 100644 avm/ptn/mgmt-groups/subscription-placement/main.json create mode 100644 avm/ptn/mgmt-groups/subscription-placement/modules/helper.bicep create mode 100644 avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/main.test.bicep create mode 100644 avm/ptn/mgmt-groups/subscription-placement/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2b6613a51b..bad9d11c98 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -31,6 +31,7 @@ /avm/ptn/dev-ops/cicd-agents-and-runners/ @Azure/avm-ptn-devops-cicdagentsandrunners-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/finops-toolkit/finops-hub/ @Azure/avm-ptn-finopstoolkit-finopshub-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/lz/sub-vending/ @Azure/avm-ptn-lz-subvending-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/mgmt-groups/subscription-placement/ @Azure/avm-ptn-mgmtgroups-subscriptionplacement-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/network/hub-networking/ @Azure/avm-ptn-network-hubnetworking-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/network/private-link-private-dns-zones/ @Azure/avm-ptn-network-privatelinkprivatednszones-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/policy-insights/remediation/ @Azure/avm-ptn-policyinsights-remediation-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 09a2c5ab4d..b0315b0a37 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -66,6 +66,7 @@ body: - "avm/ptn/dev-ops/cicd-agents-and-runners" - "avm/ptn/finops-toolkit/finops-hub" - "avm/ptn/lz/sub-vending" + - "avm/ptn/mgmt-groups/subscription-placement" - "avm/ptn/network/hub-networking" - "avm/ptn/network/private-link-private-dns-zones" - "avm/ptn/policy-insights/remediation" diff --git a/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml b/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml new file mode 100644 index 0000000000..a55eedcb45 --- /dev/null +++ b/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml @@ -0,0 +1,84 @@ +name: "avm.ptn.mgmt-groups.subscription-placement" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml" + - "avm/ptn/lz/sub-placement/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/mgmt-groups/subscription-placement" + workflowPath: ".github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/ptn/mgmt-groups/subscription-placement/README.md b/avm/ptn/mgmt-groups/subscription-placement/README.md new file mode 100644 index 0000000000..8f1c07509a --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/README.md @@ -0,0 +1,170 @@ +# subscription-placement `[MgmtGroups/SubscriptionPlacement]` + +This module allows for placement of subscriptions to management groups + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Management/managementGroups/subscriptions` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Management/2023-04-01/managementGroups/subscriptions) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/mgmt-groups/subscription-placement:<version>`. + +- [Using only defaults.](#example-1-using-only-defaults) + +### Example 1: _Using only defaults._ + +This instance deploys the module with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module subscriptionPlacement 'br/public:avm/ptn/mgmt-groups/subscription-placement:<version>' = { + name: 'subscriptionPlacementDeployment' + params: { + parSubscriptionPlacement: [ + { + managementGroupId: '<managementGroupId>' + subscriptionIds: [ + '<subVendingSubscriptionId>' + ] + } + ] + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "parSubscriptionPlacement": { + "value": [ + { + "managementGroupId": "<managementGroupId>", + "subscriptionIds": [ + "<subVendingSubscriptionId>" + ] + } + ] + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/ptn/mgmt-groups/subscription-placement:<version>' + +param parSubscriptionPlacement = [ + { + managementGroupId: '<managementGroupId>' + subscriptionIds: [ + '<subVendingSubscriptionId>' + ] + } +] +``` + +</details> +<p> + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`parSubscriptionPlacement`](#parameter-parsubscriptionplacement) | array | The management group IDs along with the subscriptions to be placed underneath them. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location for all resources. | + +### Parameter: `parSubscriptionPlacement` + +The management group IDs along with the subscriptions to be placed underneath them. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managementGroupId`](#parameter-parsubscriptionplacementmanagementgroupid) | string | The ID of the management group. | +| [`subscriptionIds`](#parameter-parsubscriptionplacementsubscriptionids) | array | The list of subscription IDs to be placed underneath the management group. | + +### Parameter: `parSubscriptionPlacement.managementGroupId` + +The ID of the management group. + +- Required: Yes +- Type: string + +### Parameter: `parSubscriptionPlacement.subscriptionIds` + +The list of subscription IDs to be placed underneath the management group. + +- Required: Yes +- Type: array + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `subscriptionPlacementSummary` | string | Output of number of management groups that have been configured with subscription placements. | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/mgmt-groups/subscription-placement/main.bicep b/avm/ptn/mgmt-groups/subscription-placement/main.bicep new file mode 100644 index 0000000000..d80dd04d8f --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/main.bicep @@ -0,0 +1,67 @@ +targetScope = 'tenant' + +metadata name = 'subscription-placement' +metadata description = 'This module allows for placement of subscriptions to management groups ' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Required. The management group IDs along with the subscriptions to be placed underneath them.') +param parSubscriptionPlacement subscriptionPlacementType[] + +@description('Optional. Location for all resources.') +param location string = deployment().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.mgmtgroup-subplacement.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + location: location + 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' + } + } + } + } +} + +module customSubscriptionPlacement './modules/helper.bicep' = [ + for (subscriptionPlacement, index) in parSubscriptionPlacement: { + name: 'subPlacment-${uniqueString(subscriptionPlacement.managementGroupId)}${index}' + params: { + managementGroupId: subscriptionPlacement.managementGroupId + subscriptionIds: subscriptionPlacement.subscriptionIds + } + } +] + +// =============== // +// Outputs // +// =============== // + +@description('Output of number of management groups that have been configured with subscription placements.') +output subscriptionPlacementSummary string = 'Subscription placements have been configured for ${length(parSubscriptionPlacement)} management groups.' + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for a subscription placement.') +type subscriptionPlacementType = { + @description('Required. The ID of the management group.') + managementGroupId: string + @description('Required. The list of subscription IDs to be placed underneath the management group.') + subscriptionIds: string[] +} diff --git a/avm/ptn/mgmt-groups/subscription-placement/main.json b/avm/ptn/mgmt-groups/subscription-placement/main.json new file mode 100644 index 0000000000..bdd798c56b --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/main.json @@ -0,0 +1,168 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "2325138724313533895" + }, + "name": "subscription-placement", + "description": "This module allows for placement of subscriptions to management groups ", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "subscriptionPlacementType": { + "type": "object", + "properties": { + "managementGroupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the management group." + } + }, + "subscriptionIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of subscription IDs to be placed underneath the management group." + } + } + } + } + }, + "parameters": { + "parSubscriptionPlacement": { + "type": "array", + "items": { + "$ref": "#/definitions/subscriptionPlacementType" + }, + "metadata": { + "description": "Required. The management group IDs along with the subscriptions to be placed underneath them." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.ptn.mgmtgroup-subplacement.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "location": "[parameters('location')]", + "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" + } + } + } + } + }, + "customSubscriptionPlacement": { + "copy": { + "name": "customSubscriptionPlacement", + "count": "[length(parameters('parSubscriptionPlacement'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('subPlacment-{0}{1}', uniqueString(parameters('parSubscriptionPlacement')[copyIndex()].managementGroupId), copyIndex())]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managementGroupId": { + "value": "[parameters('parSubscriptionPlacement')[copyIndex()].managementGroupId]" + }, + "subscriptionIds": { + "value": "[parameters('parSubscriptionPlacement')[copyIndex()].subscriptionIds]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "10679020409212079790" + } + }, + "parameters": { + "managementGroupId": { + "type": "string", + "metadata": { + "description": "The ID of the management group." + } + }, + "subscriptionIds": { + "type": "array", + "metadata": { + "description": "The list of subscription IDs." + } + } + }, + "resources": [ + { + "copy": { + "name": "customSubscriptionPlacement", + "count": "[length(parameters('subscriptionIds'))]" + }, + "type": "Microsoft.Management/managementGroups/subscriptions", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('managementGroupId'), parameters('subscriptionIds')[copyIndex()])]" + } + ], + "outputs": { + "subscriptionPlacements": { + "type": "array", + "metadata": { + "description": "Output of the Management Group and Subscription Resource ID placements." + }, + "copy": { + "count": "[length(parameters('subscriptionIds'))]", + "input": { + "name": "[format('{0}/{1}', parameters('managementGroupId'), parameters('subscriptionIds')[copyIndex()])]" + } + } + } + } + } + } + } + }, + "outputs": { + "subscriptionPlacementSummary": { + "type": "string", + "metadata": { + "description": "Output of number of management groups that have been configured with subscription placements." + }, + "value": "[format('Subscription placements have been configured for {0} management groups.', length(parameters('parSubscriptionPlacement')))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/mgmt-groups/subscription-placement/modules/helper.bicep b/avm/ptn/mgmt-groups/subscription-placement/modules/helper.bicep new file mode 100644 index 0000000000..32476329c5 --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/modules/helper.bicep @@ -0,0 +1,19 @@ +targetScope = 'tenant' + +@description('The ID of the management group.') +param managementGroupId string +@description('The list of subscription IDs.') +param subscriptionIds string[] + +resource customSubscriptionPlacement 'Microsoft.Management/managementGroups/subscriptions@2023-04-01' = [ + for (subscription, i) in subscriptionIds: { + name: '${managementGroupId}/${subscription}' + } +] + +@description('Output of the Management Group and Subscription Resource ID placements.') +output subscriptionPlacements array = [ + for (subscription, i) in subscriptionIds: { + name: '${managementGroupId}/${subscription}' + } +] diff --git a/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/dependencies.bicep b/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..c737a20c3f --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,50 @@ +targetScope = 'tenant' + +@description('Required. The root management group resource ID where the subscriptions will be placed.') +@secure() +param rootManagementGroupResourceId string + + +@description('Required. The scope of the subscription billing.') +@secure() +param subscriptionBillingScope string + +resource managementGroup 'Microsoft.Management/managementGroups@2023-04-01' = { + name: 'test-mgmt-group' + properties: { + displayName: 'Test Management Group' + details:{ + parent:{ + id: rootManagementGroupResourceId + } + } + } +} + +module subVending 'br/public:avm/ptn/lz/sub-vending:0.2.4' = { + name: 'subVendingDeployment' + scope: managementGroup + params: { + subscriptionAliasEnabled: true + subscriptionAliasName: 'NewSubscription' + subscriptionBillingScope: subscriptionBillingScope + subscriptionDisplayName: 'NewSubscription' + subscriptionTags: { + avmTest: 'true' + } + subscriptionWorkload: 'Production' + resourceProviders: {} + } +} + +@description('Output of the management group resource ID.') +output managementGroupId string = managementGroup.id + +@description('Output of the management group name.') +output managementGroupName string = managementGroup.name + +@description('Output of the subscription vending resource ID.') +output subVendingResourceId string = subVending.outputs.subscriptionResourceId + +@description('Output of the subscription vending subscription ID.') +output subVendingSubscriptionId string = subVending.outputs.subscriptionId diff --git a/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/main.test.bicep b/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..c62243ed5d --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,52 @@ +metadata name = 'Using only defaults.' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +targetScope = 'tenant' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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 = 'subplmin' + +@description('Required. The root management group resource ID where the subscriptions will be placed. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-RootManagementGroupResourceId\'.') +@secure() +param rootManagementGroupResourceId string = '' + +@description('Required. The scope of the subscription billing. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-SubscriptionBillingScope\'.') +@secure() +param subscriptionBillingScope string = '' + +// =============== // +// Dependencies // +// =============== // + +module dependencies './dependencies.bicep' = { + name: '${uniqueString(deployment().name, namePrefix)}-dependencies-${serviceShort}' + scope: tenant() + params: { + subscriptionBillingScope: subscriptionBillingScope + rootManagementGroupResourceId: rootManagementGroupResourceId + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, namePrefix)}-test-${serviceShort}' + params: { + parSubscriptionPlacement: [ + { + managementGroupId: dependencies.outputs.managementGroupName + subscriptionIds: [ + dependencies.outputs.subVendingSubscriptionId + ] + } + ] + } +} + +@description('This output retrieves the subscription placement summary from the test deployment outputs.') +output subscriptionPlacementSummary string = testDeployment.outputs.subscriptionPlacementSummary diff --git a/avm/ptn/mgmt-groups/subscription-placement/version.json b/avm/ptn/mgmt-groups/subscription-placement/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/mgmt-groups/subscription-placement/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 3389cabf7edd71ce3a28fe1f6d8340c8e3cf5a4d Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Fri, 14 Feb 2025 12:48:23 +0100 Subject: [PATCH 31/53] fix: Subscription-placement - Regenerated ARM JSON (#4447) ## Description When the previous PR was merged it turned out the ARM JSON file was not reflecting the 'latest' JSON. This PR corrects this. | Pipeline | | - | | [![avm.ptn.mgmt-groups.subscription-placement](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.mgmt-groups.subscription-placement.yml/badge.svg?branch=users%2Falsehr%2Fmgmt-groups_subscription-placement_static&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.mgmt-groups.subscription-placement.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --- .../subscription-placement/main.json | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/avm/ptn/mgmt-groups/subscription-placement/main.json b/avm/ptn/mgmt-groups/subscription-placement/main.json index bdd798c56b..5be0d619f0 100644 --- a/avm/ptn/mgmt-groups/subscription-placement/main.json +++ b/avm/ptn/mgmt-groups/subscription-placement/main.json @@ -6,11 +6,10 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "2325138724313533895" + "templateHash": "1512617397431432841" }, "name": "subscription-placement", - "description": "This module allows for placement of subscriptions to management groups ", - "owner": "Azure/module-maintainers" + "description": "This module allows for placement of subscriptions to management groups " }, "definitions": { "subscriptionPlacementType": { @@ -31,6 +30,10 @@ "description": "Required. The list of subscription IDs to be placed underneath the management group." } } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a subscription placement." } } }, @@ -105,12 +108,13 @@ }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10679020409212079790" + "templateHash": "6413058457168295861" } }, "parameters": { @@ -122,13 +126,16 @@ }, "subscriptionIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { "description": "The list of subscription IDs." } } }, - "resources": [ - { + "resources": { + "customSubscriptionPlacement": { "copy": { "name": "customSubscriptionPlacement", "count": "[length(parameters('subscriptionIds'))]" @@ -137,7 +144,7 @@ "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('managementGroupId'), parameters('subscriptionIds')[copyIndex()])]" } - ], + }, "outputs": { "subscriptionPlacements": { "type": "array", From 77dd70956fe39b5639e9be102e718975409dbe3a Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Fri, 14 Feb 2025 15:53:56 +0100 Subject: [PATCH 32/53] fix: MgmtGroup-SubscriptionPlacement - Corrected incorrect trigger path (#4448) ## Description - Corrected incorrect trigger path - Added small change to module to trigger run ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --- .../workflows/avm.ptn.mgmt-groups.subscription-placement.yml | 2 +- avm/ptn/mgmt-groups/subscription-placement/README.md | 2 +- avm/ptn/mgmt-groups/subscription-placement/main.bicep | 2 +- avm/ptn/mgmt-groups/subscription-placement/main.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml b/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml index a55eedcb45..137f131f1c 100644 --- a/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml +++ b/.github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml @@ -25,7 +25,7 @@ on: - ".github/actions/templates/avm-**" - ".github/workflows/avm.template.module.yml" - ".github/workflows/avm.ptn.mgmt-groups.subscription-placement.yml" - - "avm/ptn/lz/sub-placement/**" + - "avm/ptn/mgmt-groups/subscription-placement/**" - "avm/utilities/pipelines/**" - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" diff --git a/avm/ptn/mgmt-groups/subscription-placement/README.md b/avm/ptn/mgmt-groups/subscription-placement/README.md index 8f1c07509a..ba133d4f50 100644 --- a/avm/ptn/mgmt-groups/subscription-placement/README.md +++ b/avm/ptn/mgmt-groups/subscription-placement/README.md @@ -1,6 +1,6 @@ # subscription-placement `[MgmtGroups/SubscriptionPlacement]` -This module allows for placement of subscriptions to management groups +This module allows for placement of subscriptions to management groups. ## Navigation diff --git a/avm/ptn/mgmt-groups/subscription-placement/main.bicep b/avm/ptn/mgmt-groups/subscription-placement/main.bicep index d80dd04d8f..15fd9e8713 100644 --- a/avm/ptn/mgmt-groups/subscription-placement/main.bicep +++ b/avm/ptn/mgmt-groups/subscription-placement/main.bicep @@ -1,7 +1,7 @@ targetScope = 'tenant' metadata name = 'subscription-placement' -metadata description = 'This module allows for placement of subscriptions to management groups ' +metadata description = 'This module allows for placement of subscriptions to management groups.' // ------------------ // PARAMETERS diff --git a/avm/ptn/mgmt-groups/subscription-placement/main.json b/avm/ptn/mgmt-groups/subscription-placement/main.json index 5be0d619f0..0bd9053d6d 100644 --- a/avm/ptn/mgmt-groups/subscription-placement/main.json +++ b/avm/ptn/mgmt-groups/subscription-placement/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "1512617397431432841" + "templateHash": "12502542864893705083" }, "name": "subscription-placement", - "description": "This module allows for placement of subscriptions to management groups " + "description": "This module allows for placement of subscriptions to management groups." }, "definitions": { "subscriptionPlacementType": { From e2a2a34408be3064e09b58f65c92bb5f9c46e217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= <rene@hezser.de> Date: Fri, 14 Feb 2025 16:00:57 +0100 Subject: [PATCH 33/53] fix: `res/insights/private-link-scope` deploys private endpoints in the subnet resource group (#4132) ## Description - Adds UDTs - deploys private endpoint in the subnet resource group, if not explicitly specified Closes #3835 #4404 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.insights.private-link-scope](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=issue-3835)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] 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 --- avm/res/insights/private-link-scope/README.md | 51 +- .../insights/private-link-scope/main.bicep | 186 +-- avm/res/insights/private-link-scope/main.json | 1232 +++++++++-------- .../scoped-resource/main.json | 4 +- .../tests/e2e/defaults/main.test.bicep | 2 +- .../tests/e2e/max/dependencies.bicep | 8 +- .../tests/e2e/max/main.test.bicep | 5 +- .../tests/e2e/waf-aligned/dependencies.bicep | 6 +- .../tests/e2e/waf-aligned/main.test.bicep | 5 +- .../insights/private-link-scope/version.json | 4 +- 10 files changed, 761 insertions(+), 742 deletions(-) diff --git a/avm/res/insights/private-link-scope/README.md b/avm/res/insights/private-link-scope/README.md index 4f0d01138d..16fbdbbff9 100644 --- a/avm/res/insights/private-link-scope/README.md +++ b/avm/res/insights/private-link-scope/README.md @@ -1277,22 +1277,22 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the Private Endpoint IP configuration is included. | | [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | -| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | +| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the Private Endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. | | [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | -| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | +| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the Private Endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | -| [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`name`](#parameter-privateendpointsname) | string | The name of the Private Endpoint. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS Zone Group to configure for the Private Endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | -| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | +| [`resourceGroupResourceId`](#parameter-privateendpointsresourcegroupresourceid) | string | The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | -| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | -| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. | +| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/Resource Groups in this deployment. | ### Parameter: `privateEndpoints.subnetResourceId` @@ -1303,7 +1303,7 @@ Resource ID of the subnet where the endpoint needs to be created. ### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` -Application security groups in which the private endpoint IP configuration is included. +Application security groups in which the Private Endpoint IP configuration is included. - Required: No - Type: array @@ -1343,7 +1343,7 @@ FQDN that resolves to private endpoint IP address. ### Parameter: `privateEndpoints.customNetworkInterfaceName` -The custom name of the network interface attached to the private endpoint. +The custom name of the network interface attached to the Private Endpoint. - Required: No - Type: string @@ -1357,7 +1357,7 @@ Enable/Disable usage telemetry for module. ### Parameter: `privateEndpoints.ipConfigurations` -A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. +A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. - Required: No - Type: array @@ -1421,7 +1421,7 @@ If Manual Private Link Connection is required. ### Parameter: `privateEndpoints.location` -The location to deploy the private endpoint to. +The location to deploy the Private Endpoint to. - Required: No - Type: string @@ -1471,14 +1471,14 @@ A message passed to the owner of the remote resource with the manual connection ### Parameter: `privateEndpoints.name` -The name of the private endpoint. +The name of the Private Endpoint. - Required: No - Type: string ### Parameter: `privateEndpoints.privateDnsZoneGroup` -The private DNS zone group to configure for the private endpoint. +The private DNS Zone Group to configure for the Private Endpoint. - Required: No - Type: object @@ -1487,7 +1487,7 @@ The private DNS zone group to configure for the private endpoint. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. | **Optional parameters** @@ -1497,7 +1497,7 @@ The private DNS zone group to configure for the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` -The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. +The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. - Required: Yes - Type: array @@ -1512,7 +1512,7 @@ The private DNS zone groups to associate the private endpoint. A DNS zone group | Parameter | Type | Description | | :-- | :-- | :-- | -| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS Zone Group config. | ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` @@ -1523,7 +1523,7 @@ The resource id of the private DNS zone. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group config. +The name of the private DNS Zone Group config. - Required: No - Type: string @@ -1542,9 +1542,9 @@ The name of the private link connection to create. - Required: No - Type: string -### Parameter: `privateEndpoints.resourceGroupName` +### Parameter: `privateEndpoints.resourceGroupResourceId` -Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. +The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. - Required: No - Type: string @@ -1565,7 +1565,7 @@ Array of role assignments to create. - `'Owner'` - `'Private DNS Zone Contributor'` - `'Reader'` - - `'Role Based Access Control Administrator (Preview)'` + - `'Role Based Access Control Administrator'` **Required parameters** @@ -1659,14 +1659,14 @@ The principal type of the assigned principal ID. ### Parameter: `privateEndpoints.service` -The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". +The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. - Required: No - Type: string ### Parameter: `privateEndpoints.tags` -Tags to be applied on all resources/resource groups in this deployment. +Tags to be applied on all resources/Resource Groups in this deployment. - Required: No - Type: object @@ -1834,7 +1834,8 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/insights/private-link-scope/main.bicep b/avm/res/insights/private-link-scope/main.bicep index f0d1c0601a..10acf70b51 100644 --- a/avm/res/insights/private-link-scope/main.bicep +++ b/avm/res/insights/private-link-scope/main.bicep @@ -9,22 +9,25 @@ param name string * Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured. * Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode.''') -param accessModeSettings accessModeType +param accessModeSettings accessModeType? @description('Optional. The location of the private link scope. Should be global.') param location string = 'global' +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? @description('Optional. Configuration details for Azure Monitor Resources.') -param scopedResources scopedResourceType +param scopedResources scopedResourceType[]? +import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') -param privateEndpoints privateEndpointType +param privateEndpoints privateEndpointSingleServiceType[]? @description('Optional. Resource tags.') param tags object? @@ -117,17 +120,15 @@ resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-previ location: location tags: tags properties: { - accessModeSettings: !empty(privateEndpoints) + accessModeSettings: accessModeSettings ?? (!empty(privateEndpoints) ? { - ingestionAccessMode: accessModeSettings.?ingestionAccessMode ?? 'PrivateOnly' - queryAccessMode: accessModeSettings.?queryAccessMode ?? 'PrivateOnly' - exclusions: accessModeSettings.?exclusions ?? [] - } - : accessModeSettings ?? { - ingestionAccessMode: accessModeSettings.?ingestionAccessMode ?? 'Open' - queryAccessMode: accessModeSettings.?queryAccessMode ?? 'Open' - exclusions: accessModeSettings.?exclusions ?? [] + ingestionAccessMode: 'PrivateOnly' + queryAccessMode: 'PrivateOnly' } + : { + ingestionAccessMode: 'Open' + queryAccessMode: 'Open' + }) } } @@ -153,10 +154,14 @@ resource privateLinkScope_lock 'Microsoft.Authorization/locks@2020-05-01' = if ( scope: privateLinkScope } -module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ +module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-privateLinkScope-PrivateEndpoint-${index}' - scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + // use the subnet resource group if the resource group is not explicitly provided + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(privateLinkScope.id, '/'))}-${privateEndpoint.?service ?? 'azuremonitor'}-${index}' privateLinkServiceConnections: privateEndpoint.?isManualConnection != true @@ -237,13 +242,13 @@ output resourceGroupName string = resourceGroup().name output location string = privateLinkScope.location @description('The private endpoints of the private link scope.') -output privateEndpoints array = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: privateLinkScope_privateEndpoints[i].outputs.name - resourceId: privateLinkScope_privateEndpoints[i].outputs.resourceId - groupId: privateLinkScope_privateEndpoints[i].outputs.groupId - customDnsConfig: privateLinkScope_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: privateLinkScope_privateEndpoints[i].outputs.networkInterfaceIds +output privateEndpoints privateEndpointOutputType[] = [ + for (item, index) in (privateEndpoints ?? []): { + name: privateLinkScope_privateEndpoints[index].outputs.name + resourceId: privateLinkScope_privateEndpoints[index].outputs.resourceId + groupId: privateLinkScope_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: privateLinkScope_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: privateLinkScope_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] @@ -251,135 +256,42 @@ output privateEndpoints array = [ // Definitions // // =============== // -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - 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')? - - @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('Optional. The name of the private link connection to create.') - privateLinkServiceConnectionName: string? - - @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') - service: string? - - @description('Required. Resource ID of the subnet where the endpoint needs to be created.') - subnetResourceId: string - - @description('Optional. The private DNS zone group to configure for the private endpoint.') - privateDnsZoneGroup: { - @description('Optional. The name of the Private DNS Zone Group.') - name: string? - - @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneGroupConfigs: { - @description('Optional. The name of the private DNS zone group config.') - name: string? - - @description('Required. The resource id of the private DNS zone.') - privateDnsZoneResourceId: string - }[] - }? +@export() +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string - @description('Optional. If Manual Private Link Connection is required.') - isManualConnection: bool? + @description('The resource ID of the private endpoint.') + resourceId: string - @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') - @maxLength(140) - manualConnectionRequestMessage: string? + @description('The group Id for the private endpoint Group.') + groupId: string? - @description('Optional. Custom DNS configurations.') + @description('The custom DNS configurations of the private endpoint.') customDnsConfigs: { - @description('Optional. FQDN that resolves to private endpoint IP address.') + @description('FQDN that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private IP addresses of the private endpoint.') + @description('A list of private IP addresses of the private endpoint.') 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: { - @description('Required. The name of the resource that is unique within a resource group.') - name: string - - @description('Required. Properties of private endpoint IP configurations.') - properties: { - @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') - groupId: string - - @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') - memberName: string - - @description('Required. A private IP address obtained from the private endpoint\'s subnet.') - 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: lockType - - @description('Optional. Array of role assignments to create.') - roleAssignments: roleAssignmentType - - @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') - tags: object? - - @description('Optional. Enable/Disable usage telemetry for module.') - enableTelemetry: bool? - - @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') - resourceGroupName: string? -}[]? + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} +@export() +@description('The scoped resource type.') type scopedResourceType = { @description('Required. Name of the private link scoped resource.') name: string @description('Required. The resource ID of the scoped Azure monitor resource.') linkedResourceId: string -}[]? +} +@export() +@description('The access mode type.') type accessModeType = { @description('Optional. List of exclusions that override the default access mode settings for specific private endpoint connections. Exclusions for the current created Private endpoints can only be applied post initial provisioning.') exclusions: { @@ -398,4 +310,4 @@ type accessModeType = { @description('Optional. Specifies the default access mode of queries through associated private endpoints in scope. Default is "Open" if no private endpoints are configured and will be set to "PrivateOnly" if private endpoints are configured. Override default behaviour by explicitly providing a value.') queryAccessMode: 'Open' | 'PrivateOnly'? -}? +} diff --git a/avm/res/insights/private-link-scope/main.json b/avm/res/insights/private-link-scope/main.json index 7717146bfa..18e2c4753b 100644 --- a/avm/res/insights/private-link-scope/main.json +++ b/avm/res/insights/private-link-scope/main.json @@ -5,351 +5,96 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "8291521551754556445" + "version": "0.33.93.31351", + "templateHash": "11329844199871738536" }, "name": "Azure Monitor Private Link Scopes", "description": "This module deploys an Azure Monitor Private Link Scope." }, "definitions": { - "lockType": { + "privateEndpointOutputType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "The name of the private endpoint." } }, - "kind": { + "resourceId": { "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, "metadata": { - "description": "Optional. Specify the type of lock." + "description": "The resource ID of the private endpoint." } - } - }, - "nullable": true - }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." } - } - }, - "nullable": true - }, - "privateEndpointType": { - "type": "array", - "items": { - "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." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { + }, + "customDnsConfigs": { + "type": "array", + "items": { "type": "object", "properties": { - "name": { + "fqdn": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "FQDN that resolves to private endpoint IP address." } }, - "privateDnsZoneGroupConfigs": { + "ipAddresses": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group config." - } - }, - "privateDnsZoneResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of the private DNS zone." - } - } - } + "type": "string" }, "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "A list of private IP addresses of the private endpoint." } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." } }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "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": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." - } + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "scopedResourceType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private link scoped resource." - } - }, - "linkedResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the scoped Azure monitor resource." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private link scoped resource." + } + }, + "linkedResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the scoped Azure monitor resource." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The scoped resource type." + } }, "accessModeType": { "type": "object", @@ -415,7 +160,366 @@ } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The access mode type." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "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." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "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": { + "$ref": "#/definitions/lockType", + "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 assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } } }, "parameters": { @@ -428,6 +532,7 @@ }, "accessModeSettings": { "$ref": "#/definitions/accessModeType", + "nullable": true, "metadata": { "description": "Optional. Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.\n\n* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.\n* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode." } @@ -441,24 +546,37 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } }, "scopedResources": { - "$ref": "#/definitions/scopedResourceType", + "type": "array", + "items": { + "$ref": "#/definitions/scopedResourceType" + }, + "nullable": true, "metadata": { "description": "Optional. Configuration details for Azure Monitor Resources." } }, "privateEndpoints": { - "$ref": "#/definitions/privateEndpointType", + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, "metadata": { "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } @@ -531,7 +649,7 @@ "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "accessModeSettings": "[if(not(empty(parameters('privateEndpoints'))), createObject('ingestionAccessMode', coalesce(tryGet(parameters('accessModeSettings'), 'ingestionAccessMode'), 'PrivateOnly'), 'queryAccessMode', coalesce(tryGet(parameters('accessModeSettings'), 'queryAccessMode'), 'PrivateOnly'), 'exclusions', coalesce(tryGet(parameters('accessModeSettings'), 'exclusions'), createArray())), coalesce(parameters('accessModeSettings'), createObject('ingestionAccessMode', coalesce(tryGet(parameters('accessModeSettings'), 'ingestionAccessMode'), 'Open'), 'queryAccessMode', coalesce(tryGet(parameters('accessModeSettings'), 'queryAccessMode'), 'Open'), 'exclusions', coalesce(tryGet(parameters('accessModeSettings'), 'exclusions'), createArray()))))]" + "accessModeSettings": "[coalesce(parameters('accessModeSettings'), if(not(empty(parameters('privateEndpoints'))), createObject('ingestionAccessMode', 'PrivateOnly', 'queryAccessMode', 'PrivateOnly'), createObject('ingestionAccessMode', 'Open', 'queryAccessMode', 'Open')))]" } }, "privateLinkScope_lock": { @@ -600,8 +718,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15902182804479467110" + "version": "0.33.93.31351", + "templateHash": "18319290317274254807" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource." @@ -675,7 +793,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-privateLinkScope-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -728,12 +847,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -755,80 +873,118 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "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\"." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "lockType": { "type": "object", @@ -853,182 +1009,108 @@ } } }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } - }, - "nullable": true + } }, - "privateDnsZoneGroupConfigType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "privateDnsZoneResourceId": { + "roleDefinitionIdOrName": { "type": "string", "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, "metadata": { + "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -1048,6 +1130,9 @@ }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -1061,7 +1146,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -1082,12 +1171,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -1100,21 +1194,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -1143,7 +1249,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -1151,7 +1257,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1257,12 +1363,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -1340,10 +1445,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -1405,26 +1507,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -1466,17 +1575,20 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the private link scope." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } } diff --git a/avm/res/insights/private-link-scope/scoped-resource/main.json b/avm/res/insights/private-link-scope/scoped-resource/main.json index 318184f1f4..205d815637 100644 --- a/avm/res/insights/private-link-scope/scoped-resource/main.json +++ b/avm/res/insights/private-link-scope/scoped-resource/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15902182804479467110" + "version": "0.33.93.31351", + "templateHash": "18319290317274254807" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource." diff --git a/avm/res/insights/private-link-scope/tests/e2e/defaults/main.test.bicep b/avm/res/insights/private-link-scope/tests/e2e/defaults/main.test.bicep index 122e677443..ce9ad59fd3 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/defaults/main.test.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/defaults/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/private-link-scope/tests/e2e/max/dependencies.bicep b/avm/res/insights/private-link-scope/tests/e2e/max/dependencies.bicep index e09c9b5a0c..9777bf7f95 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/max/dependencies.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/max/dependencies.bicep @@ -32,16 +32,16 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } } -resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: managedIdentityName location: location } -resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2024-06-01' = { name: 'privatelink.monitor.azure.com' location: 'global' - resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + resource virtualNetworkLinks 'virtualNetworkLinks@2024-06-01' = { name: '${virtualNetwork.name}-vnetlink' location: 'global' properties: { @@ -53,7 +53,7 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { } } -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } diff --git a/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep b/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep index 25c575033e..1518706bee 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -221,8 +221,5 @@ module testDeployment '../../../main.bicep' = [ name: 'myCustomLockName' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep index 83e5706e42..98de113b84 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/dependencies.bicep @@ -29,11 +29,11 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } } -resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2024-06-01' = { name: 'privatelink.monitor.azure.com' location: 'global' - resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + resource virtualNetworkLinks 'virtualNetworkLinks@2024-06-01' = { name: '${virtualNetwork.name}-vnetlink' location: 'global' properties: { @@ -45,7 +45,7 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { } } -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } diff --git a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep index 5a537cd3fe..22f340ac28 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -175,8 +175,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/insights/private-link-scope/version.json b/avm/res/insights/private-link-scope/version.json index a8eda31021..9ed3662aba 100644 --- a/avm/res/insights/private-link-scope/version.json +++ b/avm/res/insights/private-link-scope/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From b24cd7684c6015c31542f6c7c216a8c2677c2ed7 Mon Sep 17 00:00:00 2001 From: Salima-90 <103124614+Salima-90@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:19:08 +0000 Subject: [PATCH 34/53] feat: update net apps api and modify volumes to deploy migration volumes (#4444) ## Description <!-- >Thank you for your contribution ! > Please include a summary of the change and which issue is fixed. > Please also include the context. > List any dependencies that are required for this change. Fixes #123 Fixes #456 Closes #123 Closes #456 --> ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.net-app.net-app-account](https://github.com/Salima-90/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml/badge.svg)](https://github.com/Salima-90/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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. - [x] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/res/net-app/net-app-account/README.md | 36 +++++----- .../net-app-account/capacity-pool/README.md | 34 +++++----- .../net-app-account/capacity-pool/main.bicep | 6 +- .../net-app-account/capacity-pool/main.json | 51 ++++++++------- .../capacity-pool/volume/README.md | 49 +++++++------- .../capacity-pool/volume/main.bicep | 41 +++++++----- .../capacity-pool/volume/main.json | 35 +++++----- avm/res/net-app/net-app-account/main.json | 65 +++++++++++-------- .../snapshot-policies/README.md | 2 +- .../snapshot-policies/main.bicep | 4 +- .../snapshot-policies/main.json | 6 +- avm/res/net-app/net-app-account/version.json | 4 +- 12 files changed, 182 insertions(+), 151 deletions(-) diff --git a/avm/res/net-app/net-app-account/README.md b/avm/res/net-app/net-app-account/README.md index 514345e5bc..19f4655249 100644 --- a/avm/res/net-app/net-app-account/README.md +++ b/avm/res/net-app/net-app-account/README.md @@ -21,9 +21,9 @@ This module deploys an Azure NetApp File. | `Microsoft.NetApp/netAppAccounts/backupPolicies` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/backupPolicies) | | `Microsoft.NetApp/netAppAccounts/backupVaults` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/backupVaults) | | `Microsoft.NetApp/netAppAccounts/backupVaults/backups` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/backupVaults/backups) | -| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/capacityPools) | | `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/capacityPools/volumes) | -| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/snapshotPolicies) | +| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/snapshotPolicies) | ## Usage examples @@ -1748,8 +1748,6 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`endpointType`](#parameter-capacitypoolsvolumesdataprotectionreplicationendpointtype) | string | Indicates whether the local volume is the source or destination for the Volume Replication. | -| [`remoteVolumeRegion`](#parameter-capacitypoolsvolumesdataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication. | -| [`remoteVolumeResourceId`](#parameter-capacitypoolsvolumesdataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. | | [`replicationSchedule`](#parameter-capacitypoolsvolumesdataprotectionreplicationreplicationschedule) | string | The replication schedule for the volume. | **Optional parameters** @@ -1757,6 +1755,8 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`remotePath`](#parameter-capacitypoolsvolumesdataprotectionreplicationremotepath) | object | The full path to a volume that is to be migrated into ANF. Required for Migration volumes. | +| [`remoteVolumeRegion`](#parameter-capacitypoolsvolumesdataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication.Required for Data Protection volumes. | +| [`remoteVolumeResourceId`](#parameter-capacitypoolsvolumesdataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. Required for Data Protection volumes. | ### Parameter: `capacityPools.volumes.dataProtection.replication.endpointType` @@ -1772,20 +1772,6 @@ Indicates whether the local volume is the source or destination for the Volume R ] ``` -### Parameter: `capacityPools.volumes.dataProtection.replication.remoteVolumeRegion` - -The remote region for the other end of the Volume Replication. - -- Required: Yes -- Type: string - -### Parameter: `capacityPools.volumes.dataProtection.replication.remoteVolumeResourceId` - -The resource ID of the remote volume. - -- Required: Yes -- Type: string - ### Parameter: `capacityPools.volumes.dataProtection.replication.replicationSchedule` The replication schedule for the volume. @@ -1837,6 +1823,20 @@ The name of a volume on the server. - Required: Yes - Type: string +### Parameter: `capacityPools.volumes.dataProtection.replication.remoteVolumeRegion` + +The remote region for the other end of the Volume Replication.Required for Data Protection volumes. + +- Required: No +- Type: string + +### Parameter: `capacityPools.volumes.dataProtection.replication.remoteVolumeResourceId` + +The resource ID of the remote volume. Required for Data Protection volumes. + +- Required: No +- Type: string + ### Parameter: `capacityPools.volumes.dataProtection.snapshot` Snapshot properties. diff --git a/avm/res/net-app/net-app-account/capacity-pool/README.md b/avm/res/net-app/net-app-account/capacity-pool/README.md index 2d5f2b7bd1..092e072e7e 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/README.md @@ -14,7 +14,7 @@ This module deploys an Azure NetApp Files Capacity Pool. | 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.NetApp/netAppAccounts/capacityPools` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/capacityPools) | | `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/capacityPools/volumes) | ## Parameters @@ -390,8 +390,6 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`endpointType`](#parameter-volumesdataprotectionreplicationendpointtype) | string | Indicates whether the local volume is the source or destination for the Volume Replication. | -| [`remoteVolumeRegion`](#parameter-volumesdataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication. | -| [`remoteVolumeResourceId`](#parameter-volumesdataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. | | [`replicationSchedule`](#parameter-volumesdataprotectionreplicationreplicationschedule) | string | The replication schedule for the volume. | **Optional parameters** @@ -399,6 +397,8 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`remotePath`](#parameter-volumesdataprotectionreplicationremotepath) | object | The full path to a volume that is to be migrated into ANF. Required for Migration volumes. | +| [`remoteVolumeRegion`](#parameter-volumesdataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication.Required for Data Protection volumes. | +| [`remoteVolumeResourceId`](#parameter-volumesdataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. Required for Data Protection volumes. | ### Parameter: `volumes.dataProtection.replication.endpointType` @@ -414,20 +414,6 @@ Indicates whether the local volume is the source or destination for the Volume R ] ``` -### Parameter: `volumes.dataProtection.replication.remoteVolumeRegion` - -The remote region for the other end of the Volume Replication. - -- Required: Yes -- Type: string - -### Parameter: `volumes.dataProtection.replication.remoteVolumeResourceId` - -The resource ID of the remote volume. - -- Required: Yes -- Type: string - ### Parameter: `volumes.dataProtection.replication.replicationSchedule` The replication schedule for the volume. @@ -479,6 +465,20 @@ The name of a volume on the server. - Required: Yes - Type: string +### Parameter: `volumes.dataProtection.replication.remoteVolumeRegion` + +The remote region for the other end of the Volume Replication.Required for Data Protection volumes. + +- Required: No +- Type: string + +### Parameter: `volumes.dataProtection.replication.remoteVolumeResourceId` + +The resource ID of the remote volume. Required for Data Protection volumes. + +- Required: No +- Type: string + ### Parameter: `volumes.dataProtection.snapshot` Snapshot properties. diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/main.bicep index 69d3185518..16f124f16f 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/main.bicep @@ -74,11 +74,11 @@ var formattedRoleAssignments = [ }) ] -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-07-01' existing = { name: netAppAccountName } -resource capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2024-03-01' = { +resource capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2024-07-01' = { name: name parent: netAppAccount location: location @@ -112,7 +112,7 @@ module capacityPool_volumes 'volume/main.bicep' = [ zones: volume.?zones coolAccess: volume.?coolAccess ?? false coolAccessRetrievalPolicy: volume.?coolAccessRetrievalPolicy - coolnessPeriod: volume.?coolnessPeriod ?? 0 + coolnessPeriod: volume.?coolnessPeriod encryptionKeySource: volume.?encryptionKeySource ?? 'Microsoft.NetApp' keyVaultPrivateEndpointResourceId: volume.?keyVaultPrivateEndpointResourceId dataProtection: volume.?dataProtection diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.json b/avm/res/net-app/net-app-account/capacity-pool/main.json index 1baa0db593..6c3e2b32c1 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10620001426687567729" + "templateHash": "9255104616647372037" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool." @@ -241,14 +241,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -658,12 +660,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "capacityPool": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -759,7 +761,7 @@ "value": "[tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolAccessRetrievalPolicy')]" }, "coolnessPeriod": { - "value": "[coalesce(tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolnessPeriod'), 0)]" + "value": "[tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolnessPeriod')]" }, "encryptionKeySource": { "value": "[coalesce(tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'encryptionKeySource'), 'Microsoft.NetApp')]" @@ -794,7 +796,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14921990871750776609" + "templateHash": "13717060947862126529" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume." @@ -845,14 +847,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -1166,13 +1170,14 @@ }, "coolnessPeriod": { "type": "int", + "nullable": true, "metadata": { - "description": "Required. Specifies the number of days after which data that is not accessed by clients will be tiered." + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." } }, "coolAccessRetrievalPolicy": { "type": "string", - "defaultValue": "Default", + "nullable": true, "metadata": { "description": "Optional. Determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." } @@ -1328,6 +1333,8 @@ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], + "remoteCapacityPoolName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], '')]", + "remoteNetAppName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], '')]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", @@ -1340,7 +1347,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]" }, "netAppAccount::backupVault": { @@ -1354,18 +1361,18 @@ "condition": "[not(empty(tryGet(parameters('dataProtection'), 'backup')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName'))]" }, "netAppAccount::snapshotPolicy": { "condition": "[not(empty(tryGet(parameters('dataProtection'), 'snapshot')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))]" }, "remoteNetAppAccount::remoteCapacityPool::remoteVolume": { - "condition": "[and(and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication')))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName'))))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", "apiVersion": "2024-07-01", @@ -1374,10 +1381,10 @@ "name": "[format('{0}/{1}/{2}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/')))]" }, "remoteNetAppAccount::remoteCapacityPool": { - "condition": "[and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName')))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[format('{0}/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10])]" @@ -1393,7 +1400,7 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "keyVaultPrivateEndpoint": { @@ -1406,10 +1413,10 @@ "name": "[last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/'))]" }, "remoteNetAppAccount": { - "condition": "[not(empty(tryGet(parameters('dataProtection'), 'replication')))]", + "condition": "[and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8]]" @@ -1427,7 +1434,7 @@ "apiVersion": "2024-07-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", - "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), 'remoteVolumeResourceId', tryGet(parameters('dataProtection'), 'replication', 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), null()), 'remoteVolumeResourceId', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", "zones": "[map(parameters('zones'), lambda('zone', format('{0}', lambdaVariables('zone'))))]" }, "volume_roleAssignments": { @@ -1517,7 +1524,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('capacityPool', '2024-03-01', 'full').location]" + "value": "[reference('capacityPool', '2024-07-01', 'full').location]" }, "volumeResourceIds": { "type": "array", diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md index 2b4a0116b2..26213e73b1 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md @@ -23,7 +23,6 @@ This module deploys an Azure NetApp Files Capacity Pool Volume. | Parameter | Type | Description | | :-- | :-- | :-- | | [`coolAccess`](#parameter-coolaccess) | bool | If enabled (true) the pool can contain cool Access enabled volumes. | -| [`coolnessPeriod`](#parameter-coolnessperiod) | int | Specifies the number of days after which data that is not accessed by clients will be tiered. | | [`encryptionKeySource`](#parameter-encryptionkeysource) | string | The source of the encryption key. | | [`name`](#parameter-name) | string | The name of the pool volume. | | [`subnetResourceId`](#parameter-subnetresourceid) | string | The Azure Resource URI for a delegated subnet. Must have the delegation Microsoft.NetApp/volumes. | @@ -41,6 +40,7 @@ This module deploys an Azure NetApp Files Capacity Pool Volume. | Parameter | Type | Description | | :-- | :-- | :-- | | [`coolAccessRetrievalPolicy`](#parameter-coolaccessretrievalpolicy) | string | Determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read). | +| [`coolnessPeriod`](#parameter-coolnessperiod) | int | Specifies the number of days after which data that is not accessed by clients will be tiered. | | [`creationToken`](#parameter-creationtoken) | string | A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription. | | [`dataProtection`](#parameter-dataprotection) | object | DataProtection type volumes include an object containing details of the replication. | | [`exportPolicy`](#parameter-exportpolicy) | object | The export policy rules. | @@ -64,13 +64,6 @@ If enabled (true) the pool can contain cool Access enabled volumes. - Required: Yes - Type: bool -### Parameter: `coolnessPeriod` - -Specifies the number of days after which data that is not accessed by clients will be tiered. - -- Required: Yes -- Type: int - ### Parameter: `encryptionKeySource` The source of the encryption key. @@ -119,7 +112,13 @@ Determines the data retrieval behavior from the cool tier to standard storage ba - Required: No - Type: string -- Default: `'Default'` + +### Parameter: `coolnessPeriod` + +Specifies the number of days after which data that is not accessed by clients will be tiered. + +- Required: No +- Type: int ### Parameter: `creationToken` @@ -192,8 +191,6 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`endpointType`](#parameter-dataprotectionreplicationendpointtype) | string | Indicates whether the local volume is the source or destination for the Volume Replication. | -| [`remoteVolumeRegion`](#parameter-dataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication. | -| [`remoteVolumeResourceId`](#parameter-dataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. | | [`replicationSchedule`](#parameter-dataprotectionreplicationreplicationschedule) | string | The replication schedule for the volume. | **Optional parameters** @@ -201,6 +198,8 @@ Replication properties. | Parameter | Type | Description | | :-- | :-- | :-- | | [`remotePath`](#parameter-dataprotectionreplicationremotepath) | object | The full path to a volume that is to be migrated into ANF. Required for Migration volumes. | +| [`remoteVolumeRegion`](#parameter-dataprotectionreplicationremotevolumeregion) | string | The remote region for the other end of the Volume Replication.Required for Data Protection volumes. | +| [`remoteVolumeResourceId`](#parameter-dataprotectionreplicationremotevolumeresourceid) | string | The resource ID of the remote volume. Required for Data Protection volumes. | ### Parameter: `dataProtection.replication.endpointType` @@ -216,20 +215,6 @@ Indicates whether the local volume is the source or destination for the Volume R ] ``` -### Parameter: `dataProtection.replication.remoteVolumeRegion` - -The remote region for the other end of the Volume Replication. - -- Required: Yes -- Type: string - -### Parameter: `dataProtection.replication.remoteVolumeResourceId` - -The resource ID of the remote volume. - -- Required: Yes -- Type: string - ### Parameter: `dataProtection.replication.replicationSchedule` The replication schedule for the volume. @@ -281,6 +266,20 @@ The name of a volume on the server. - Required: Yes - Type: string +### Parameter: `dataProtection.replication.remoteVolumeRegion` + +The remote region for the other end of the Volume Replication.Required for Data Protection volumes. + +- Required: No +- Type: string + +### Parameter: `dataProtection.replication.remoteVolumeResourceId` + +The resource ID of the remote volume. Required for Data Protection volumes. + +- Required: No +- Type: string + ### Parameter: `dataProtection.snapshot` Snapshot properties. diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep index ae948d7639..f61ed7b976 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep @@ -16,11 +16,11 @@ param location string = resourceGroup().location @description('Required. If enabled (true) the pool can contain cool Access enabled volumes.') param coolAccess bool -@description('Required. Specifies the number of days after which data that is not accessed by clients will be tiered.') -param coolnessPeriod int +@description('Optional. Specifies the number of days after which data that is not accessed by clients will be tiered.') +param coolnessPeriod int? @description('Optional. Determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read).') -param coolAccessRetrievalPolicy string = 'Default' +param coolAccessRetrievalPolicy string? @description('Required. The source of the encryption key.') param encryptionKeySource string @@ -90,6 +90,13 @@ param smbNonBrowsable string = 'Disabled' @description('Optional. Define if a volume is KerberosEnabled.') param kerberosEnabled bool = false +var remoteCapacityPoolName = !empty(dataProtection.?replication.?remoteVolumeResourceId) + ? split((dataProtection.?replication.?remoteVolumeResourceId ?? '//'), '/')[10] + : '' +var remoteNetAppName = !empty(dataProtection.?replication.?remoteVolumeResourceId) + ? split((dataProtection.?replication.?remoteVolumeResourceId ?? '//'), '/')[8] + : '' + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') @@ -115,10 +122,11 @@ var formattedRoleAssignments = [ }) ] -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-07-01' existing = { name: netAppAccountName - resource capacityPool 'capacityPools@2024-03-01' existing = { + //cp-na-anfs-swc-y01 + resource capacityPool 'capacityPools@2024-07-01' existing = { name: capacityPoolName } @@ -126,11 +134,11 @@ resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { name: dataProtection.?backup!.backupVaultName } - resource backupPolicy 'backupPolicies@2024-03-01' existing = if (!empty(dataProtection.?backup)) { + resource backupPolicy 'backupPolicies@2024-07-01' existing = if (!empty(dataProtection.?backup)) { name: dataProtection.?backup!.backupPolicyName } - resource snapshotPolicy 'snapshotPolicies@2024-03-01' existing = if (!empty(dataProtection.?snapshot)) { + resource snapshotPolicy 'snapshotPolicies@2024-07-01' existing = if (!empty(dataProtection.?snapshot)) { name: dataProtection.?snapshot!.snapshotPolicyName } } @@ -143,14 +151,15 @@ resource keyVaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-03-01' ) } -resource remoteNetAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = if (!empty(dataProtection.?replication)) { +resource remoteNetAppAccount 'Microsoft.NetApp/netAppAccounts@2024-07-01' existing = if (!empty(dataProtection.?replication.?remoteVolumeResourceId) && (remoteNetAppName != netAppAccountName)) { name: split((dataProtection.?replication.?remoteVolumeResourceId ?? '//'), '/')[8] scope: resourceGroup( split((dataProtection.?replication.?remoteVolumeResourceId ?? '//'), '/')[2], split((dataProtection.?replication.?remoteVolumeResourceId ?? '////'), '/')[4] ) - resource remoteCapacityPool 'capacityPools@2024-03-01' existing = if (!empty(dataProtection.?replication)) { + //cp-na-anfs-swc-y01 + resource remoteCapacityPool 'capacityPools@2024-07-01' existing = if (!empty(dataProtection.?replication.?remoteVolumeResourceId) && (remoteCapacityPoolName != capacityPoolName)) { name: split((dataProtection.?replication.?remoteVolumeResourceId ?? '//'), '/')[10] resource remoteVolume 'volumes@2024-07-01' existing = if (!empty(dataProtection.?replication)) { @@ -192,8 +201,10 @@ resource volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2024-07-0 replication: !empty(dataProtection.?replication) ? { endpointType: dataProtection.?replication!.endpointType - remoteVolumeRegion: remoteNetAppAccount::remoteCapacityPool::remoteVolume.id - remoteVolumeResourceId: dataProtection.?replication!.remoteVolumeResourceId + remoteVolumeRegion: !empty(dataProtection.?replication.?remoteVolumeResourceId) + ? remoteNetAppAccount::remoteCapacityPool::remoteVolume.id + : null + remoteVolumeResourceId: dataProtection.?replication!.?remoteVolumeResourceId replicationSchedule: dataProtection.?replication!.replicationSchedule remotePath: dataProtection.?replication!.?remotePath } @@ -276,11 +287,11 @@ type replicationType = { @description('Required. Indicates whether the local volume is the source or destination for the Volume Replication.') endpointType: ('dst' | 'src') - @description('Required. The remote region for the other end of the Volume Replication.') - remoteVolumeRegion: string + @description('Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes.') + remoteVolumeRegion: string? - @description('Required. The resource ID of the remote volume.') - remoteVolumeResourceId: string + @description('Optional. The resource ID of the remote volume. Required for Data Protection volumes.') + remoteVolumeResourceId: string? @description('Required. The replication schedule for the volume.') replicationSchedule: ('_10minutely' | 'daily' | 'hourly') diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json index d68b83a31c..677c518485 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14921990871750776609" + "templateHash": "13717060947862126529" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume." @@ -57,14 +57,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -378,13 +380,14 @@ }, "coolnessPeriod": { "type": "int", + "nullable": true, "metadata": { - "description": "Required. Specifies the number of days after which data that is not accessed by clients will be tiered." + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." } }, "coolAccessRetrievalPolicy": { "type": "string", - "defaultValue": "Default", + "nullable": true, "metadata": { "description": "Optional. Determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." } @@ -540,6 +543,8 @@ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], + "remoteCapacityPoolName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], '')]", + "remoteNetAppName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], '')]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", @@ -552,7 +557,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]" }, "netAppAccount::backupVault": { @@ -566,18 +571,18 @@ "condition": "[not(empty(tryGet(parameters('dataProtection'), 'backup')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName'))]" }, "netAppAccount::snapshotPolicy": { "condition": "[not(empty(tryGet(parameters('dataProtection'), 'snapshot')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))]" }, "remoteNetAppAccount::remoteCapacityPool::remoteVolume": { - "condition": "[and(and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication')))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName'))))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", "apiVersion": "2024-07-01", @@ -586,10 +591,10 @@ "name": "[format('{0}/{1}/{2}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/')))]" }, "remoteNetAppAccount::remoteCapacityPool": { - "condition": "[and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName')))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[format('{0}/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10])]" @@ -605,7 +610,7 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "keyVaultPrivateEndpoint": { @@ -618,10 +623,10 @@ "name": "[last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/'))]" }, "remoteNetAppAccount": { - "condition": "[not(empty(tryGet(parameters('dataProtection'), 'replication')))]", + "condition": "[and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8]]" @@ -639,7 +644,7 @@ "apiVersion": "2024-07-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", - "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), 'remoteVolumeResourceId', tryGet(parameters('dataProtection'), 'replication', 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), null()), 'remoteVolumeResourceId', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", "zones": "[map(parameters('zones'), lambda('zone', format('{0}', lambdaVariables('zone'))))]" }, "volume_roleAssignments": { diff --git a/avm/res/net-app/net-app-account/main.json b/avm/res/net-app/net-app-account/main.json index 3d2ef51806..68b9f712e5 100644 --- a/avm/res/net-app/net-app-account/main.json +++ b/avm/res/net-app/net-app-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "15376852435816415720" + "templateHash": "3612442392718175721" }, "name": "Azure NetApp Files", "description": "This module deploys an Azure NetApp File." @@ -441,14 +441,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -1602,7 +1604,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14119927784920096194" + "templateHash": "7486219490853787459" }, "name": "Azure NetApp Files Snapshot Policy", "description": "This module deploys a Snapshot Policy for an Azure NetApp File." @@ -1843,12 +1845,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "snapshotPolicies": { "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "properties": { @@ -2267,7 +2269,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10620001426687567729" + "templateHash": "9255104616647372037" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool." @@ -2502,14 +2504,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -2919,12 +2923,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "capacityPool": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3020,7 +3024,7 @@ "value": "[tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolAccessRetrievalPolicy')]" }, "coolnessPeriod": { - "value": "[coalesce(tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolnessPeriod'), 0)]" + "value": "[tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'coolnessPeriod')]" }, "encryptionKeySource": { "value": "[coalesce(tryGet(coalesce(parameters('volumes'), createArray())[copyIndex()], 'encryptionKeySource'), 'Microsoft.NetApp')]" @@ -3055,7 +3059,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14921990871750776609" + "templateHash": "13717060947862126529" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume." @@ -3106,14 +3110,16 @@ }, "remoteVolumeRegion": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The remote region for the other end of the Volume Replication." + "description": "Optional. The remote region for the other end of the Volume Replication.Required for Data Protection volumes." } }, "remoteVolumeResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the remote volume." + "description": "Optional. The resource ID of the remote volume. Required for Data Protection volumes." } }, "replicationSchedule": { @@ -3427,13 +3433,14 @@ }, "coolnessPeriod": { "type": "int", + "nullable": true, "metadata": { - "description": "Required. Specifies the number of days after which data that is not accessed by clients will be tiered." + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." } }, "coolAccessRetrievalPolicy": { "type": "string", - "defaultValue": "Default", + "nullable": true, "metadata": { "description": "Optional. Determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." } @@ -3589,6 +3596,8 @@ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], + "remoteCapacityPoolName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], '')]", + "remoteNetAppName": "[if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], '')]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", @@ -3601,7 +3610,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]" }, "netAppAccount::backupVault": { @@ -3615,18 +3624,18 @@ "condition": "[not(empty(tryGet(parameters('dataProtection'), 'backup')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName'))]" }, "netAppAccount::snapshotPolicy": { "condition": "[not(empty(tryGet(parameters('dataProtection'), 'snapshot')))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))]" }, "remoteNetAppAccount::remoteCapacityPool::remoteVolume": { - "condition": "[and(and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication')))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName'))))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", "apiVersion": "2024-07-01", @@ -3635,10 +3644,10 @@ "name": "[format('{0}/{1}/{2}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/')))]" }, "remoteNetAppAccount::remoteCapacityPool": { - "condition": "[and(not(empty(tryGet(parameters('dataProtection'), 'replication'))), not(empty(tryGet(parameters('dataProtection'), 'replication'))))]", + "condition": "[and(and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName')))), and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteCapacityPoolName'), parameters('capacityPoolName')))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[format('{0}/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10])]" @@ -3654,7 +3663,7 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "keyVaultPrivateEndpoint": { @@ -3667,10 +3676,10 @@ "name": "[last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/'))]" }, "remoteNetAppAccount": { - "condition": "[not(empty(tryGet(parameters('dataProtection'), 'replication')))]", + "condition": "[and(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), not(equals(variables('remoteNetAppName'), parameters('netAppAccountName'))))]", "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "subscriptionId": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]]", "name": "[split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8]]" @@ -3688,7 +3697,7 @@ "apiVersion": "2024-07-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", - "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), 'remoteVolumeResourceId', tryGet(parameters('dataProtection'), 'replication', 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '//'), '/')[2], split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), '////'), '/')[4]), 'Microsoft.Network/privateEndpoints', last(split(coalesce(parameters('keyVaultPrivateEndpointResourceId'), 'dummyVault'), '/')))), createObject()), if(not(empty(parameters('volumeType'))), createObject('volumeType', parameters('volumeType')), createObject()), createObject('dataProtection', if(not(empty(parameters('dataProtection'))), createObject('replication', if(not(empty(tryGet(parameters('dataProtection'), 'replication'))), createObject('endpointType', tryGet(parameters('dataProtection'), 'replication', 'endpointType'), 'remoteVolumeRegion', if(not(empty(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[2], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '////'), '/')[4]), 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[8], split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), '//'), '/')[10], last(split(coalesce(tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'dummyvolume'), '/'))), null()), 'remoteVolumeResourceId', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remoteVolumeResourceId'), 'replicationSchedule', tryGet(parameters('dataProtection'), 'replication', 'replicationSchedule'), 'remotePath', tryGet(tryGet(parameters('dataProtection'), 'replication'), 'remotePath')), createObject()), 'backup', if(not(empty(tryGet(parameters('dataProtection'), 'backup'))), createObject('backupPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/backupPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupPolicyName')), 'policyEnforced', coalesce(tryGet(parameters('dataProtection'), 'backup', 'policyEnforced'), false()), 'backupVaultId', resourceId('Microsoft.NetApp/netAppAccounts/backupVaults', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'backup', 'backupVaultName'))), createObject()), 'snapshot', if(not(empty(tryGet(parameters('dataProtection'), 'snapshot'))), createObject('snapshotPolicyId', resourceId('Microsoft.NetApp/netAppAccounts/snapshotPolicies', parameters('netAppAccountName'), tryGet(parameters('dataProtection'), 'snapshot', 'snapshotPolicyName'))), createObject())), null()), 'networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('subnetResourceId'), '/')[2], split(parameters('subnetResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetResourceId'), '/')[8], last(split(parameters('subnetResourceId'), '/'))), 'exportPolicy', parameters('exportPolicy'), 'smbContinuouslyAvailable', parameters('smbContinuouslyAvailable'), 'smbEncryption', parameters('smbEncryption'), 'smbNonBrowsable', parameters('smbNonBrowsable'), 'kerberosEnabled', parameters('kerberosEnabled'))))]", "zones": "[map(parameters('zones'), lambda('zone', format('{0}', lambdaVariables('zone'))))]" }, "volume_roleAssignments": { @@ -3778,7 +3787,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('capacityPool', '2024-03-01', 'full').location]" + "value": "[reference('capacityPool', '2024-07-01', 'full').location]" }, "volumeResourceIds": { "type": "array", diff --git a/avm/res/net-app/net-app-account/snapshot-policies/README.md b/avm/res/net-app/net-app-account/snapshot-policies/README.md index 51bcd093fd..ca1f6c285a 100644 --- a/avm/res/net-app/net-app-account/snapshot-policies/README.md +++ b/avm/res/net-app/net-app-account/snapshot-policies/README.md @@ -12,7 +12,7 @@ This module deploys a Snapshot Policy for an Azure NetApp File. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/snapshotPolicies) | +| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-07-01/netAppAccounts/snapshotPolicies) | ## Parameters diff --git a/avm/res/net-app/net-app-account/snapshot-policies/main.bicep b/avm/res/net-app/net-app-account/snapshot-policies/main.bicep index 3bd4bee1ff..f92ae4f41a 100644 --- a/avm/res/net-app/net-app-account/snapshot-policies/main.bicep +++ b/avm/res/net-app/net-app-account/snapshot-policies/main.bicep @@ -25,11 +25,11 @@ param weeklySchedule weeklyScheduleType? @description('Optional. Indicates whether the snapshot policy is enabled.') param snapEnabled bool = false -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-07-01' existing = { name: netAppAccountName } -resource snapshotPolicies 'Microsoft.NetApp/netAppAccounts/snapshotPolicies@2024-03-01' = { +resource snapshotPolicies 'Microsoft.NetApp/netAppAccounts/snapshotPolicies@2024-07-01' = { name: name parent: netAppAccount location: location diff --git a/avm/res/net-app/net-app-account/snapshot-policies/main.json b/avm/res/net-app/net-app-account/snapshot-policies/main.json index 66bb746202..114e983832 100644 --- a/avm/res/net-app/net-app-account/snapshot-policies/main.json +++ b/avm/res/net-app/net-app-account/snapshot-policies/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14119927784920096194" + "templateHash": "7486219490853787459" }, "name": "Azure NetApp Files Snapshot Policy", "description": "This module deploys a Snapshot Policy for an Azure NetApp File." @@ -247,12 +247,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[parameters('netAppAccountName')]" }, "snapshotPolicies": { "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", - "apiVersion": "2024-03-01", + "apiVersion": "2024-07-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "properties": { diff --git a/avm/res/net-app/net-app-account/version.json b/avm/res/net-app/net-app-account/version.json index e42c3d9e5f..35040975ae 100644 --- a/avm/res/net-app/net-app-account/version.json +++ b/avm/res/net-app/net-app-account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 41e56c228d7cdcd07f83c11376227f7b35d646fa Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Fri, 14 Feb 2025 20:47:17 +0100 Subject: [PATCH 35/53] fix: Storage Account - Storage Account - Incorrect Handling of denyEncryptionScopeOverride Parameter (#4446) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Rebased from https://github.com/Azure/bicep-registry-modules/pull/4435 ### Original description: Ref: https://github.com/Azure/bicep-registry-modules/issues/4258 Issue Summary: When deploying the Storage Account module with a Blob container, the denyEncryptionScopeOverride parameter in the container's main.bicep template is incorrectly being set to null instead of explicitly retaining the expected false value when configured as such. This behavior causes potential misconfigurations when deployed on a existing storage account that already has this variable set to false, as you are not allowed to change this property after its creation. ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2FPetterHL_main_rebase&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --------- Co-authored-by: Petter Høvik Lintoft <48277303+PetterHL@users.noreply.github.com> --- .../blob-service/container/README.md | 1 - .../container/immutability-policy/main.json | 4 ++-- .../blob-service/container/main.bicep | 4 ++-- .../blob-service/container/main.json | 12 ++++++------ .../storage-account/blob-service/main.json | 16 ++++++++-------- .../storage-account/file-service/main.json | 8 ++++---- .../storage-account/file-service/share/main.json | 4 ++-- .../storage/storage-account/local-user/main.json | 4 ++-- avm/res/storage/storage-account/main.json | 10 +++++----- .../storage-account/management-policy/main.json | 4 ++-- .../storage-account/queue-service/main.json | 8 ++++---- .../queue-service/queue/main.json | 4 ++-- .../storage-account/table-service/main.json | 8 ++++---- .../table-service/table/main.json | 4 ++-- 14 files changed, 45 insertions(+), 46 deletions(-) diff --git a/avm/res/storage/storage-account/blob-service/container/README.md b/avm/res/storage/storage-account/blob-service/container/README.md index 0e912b984b..1269407b49 100644 --- a/avm/res/storage/storage-account/blob-service/container/README.md +++ b/avm/res/storage/storage-account/blob-service/container/README.md @@ -83,7 +83,6 @@ Block override of encryption scope from the container default. - Required: No - Type: bool -- Default: `False` ### Parameter: `enableNfsV3AllSquash` diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index 3e1f62c7b5..834d2b2835 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.33.13.18514", + "templateHash": "2769922037435749045" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/container/main.bicep b/avm/res/storage/storage-account/blob-service/container/main.bicep index c9dc59efb7..4bf5ee1451 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.bicep +++ b/avm/res/storage/storage-account/blob-service/container/main.bicep @@ -15,7 +15,7 @@ param name string param defaultEncryptionScope string = '' @description('Optional. Block override of encryption scope from the container default.') -param denyEncryptionScopeOverride bool = false +param denyEncryptionScopeOverride bool? @description('Optional. Enable NFSv3 all squash on blob container.') param enableNfsV3AllSquash bool = false @@ -117,7 +117,7 @@ resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@20 parent: storageAccount::blobServices properties: { defaultEncryptionScope: !empty(defaultEncryptionScope) ? defaultEncryptionScope : null - denyEncryptionScopeOverride: denyEncryptionScopeOverride == true ? denyEncryptionScopeOverride : null + denyEncryptionScopeOverride: denyEncryptionScopeOverride enableNfsV3AllSquash: enableNfsV3AllSquash == true ? enableNfsV3AllSquash : null enableNfsV3RootSquash: enableNfsV3RootSquash == true ? enableNfsV3RootSquash : null immutableStorageWithVersioning: immutableStorageWithVersioningEnabled == true diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index df35c3db1f..2cc2426989 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.33.13.18514", + "templateHash": "15727824641798999553" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -118,7 +118,7 @@ }, "denyEncryptionScopeOverride": { "type": "bool", - "defaultValue": false, + "nullable": true, "metadata": { "description": "Optional. Block override of encryption scope from the container default." } @@ -231,7 +231,7 @@ "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", - "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "denyEncryptionScopeOverride": "[parameters('denyEncryptionScopeOverride')]", "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", @@ -294,8 +294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.33.13.18514", + "templateHash": "2769922037435749045" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 4eacd0e9e2..653da1e35f 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2058460323623594433" + "version": "0.33.13.18514", + "templateHash": "13802891409950802048" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -472,8 +472,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2991444340097371621" + "version": "0.33.13.18514", + "templateHash": "15727824641798999553" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -585,7 +585,7 @@ }, "denyEncryptionScopeOverride": { "type": "bool", - "defaultValue": false, + "nullable": true, "metadata": { "description": "Optional. Block override of encryption scope from the container default." } @@ -698,7 +698,7 @@ "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", - "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "denyEncryptionScopeOverride": "[parameters('denyEncryptionScopeOverride')]", "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", @@ -761,8 +761,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8061556339565534458" + "version": "0.33.13.18514", + "templateHash": "2769922037435749045" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 95c9c54fe5..5257496990 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16196407713115246323" + "version": "0.33.13.18514", + "templateHash": "987643333058038389" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -359,8 +359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.13.18514", + "templateHash": "15193761941438215308" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index a95097e146..8d520a8279 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5204319087439022536" + "version": "0.33.13.18514", + "templateHash": "15193761941438215308" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index dac871c0e5..12583d34c6 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "16427795222629898111" + "version": "0.33.13.18514", + "templateHash": "2528560857012083896" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 0d22a420a4..9f0968f7e3 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "3424685276444889234" + "templateHash": "7065290388008718363" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -2572,7 +2572,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "14339432978450199921" + "templateHash": "13802891409950802048" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -3039,7 +3039,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10816586207828434096" + "templateHash": "15727824641798999553" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -3151,7 +3151,7 @@ }, "denyEncryptionScopeOverride": { "type": "bool", - "defaultValue": false, + "nullable": true, "metadata": { "description": "Optional. Block override of encryption scope from the container default." } @@ -3264,7 +3264,7 @@ "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", - "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "denyEncryptionScopeOverride": "[parameters('denyEncryptionScopeOverride')]", "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index bc2c2530f7..55ab734bb6 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4014848332192190169" + "version": "0.33.13.18514", + "templateHash": "4967204006599351003" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index fa4e1c19d4..994d11d297 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14497929042813606497" + "version": "0.33.13.18514", + "templateHash": "8158577333548255612" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -324,8 +324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.13.18514", + "templateHash": "9877120144610775153" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 19308bf371..a5175e5224 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9969689246600110741" + "version": "0.33.13.18514", + "templateHash": "9877120144610775153" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index c336dcaed3..c516784297 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4194630585059896468" + "version": "0.33.13.18514", + "templateHash": "541986423744885003" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -321,8 +321,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.13.18514", + "templateHash": "11234204519679347949" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index f9e2ac6377..f4ea8be690 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "4457939127962832961" + "version": "0.33.13.18514", + "templateHash": "11234204519679347949" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." From 8add4330a059cc23a44d7477bc55e2c1e4dc8224 Mon Sep 17 00:00:00 2001 From: Clint Grove <clintelyonson@gmail.com> Date: Sat, 15 Feb 2025 16:37:02 +0000 Subject: [PATCH 36/53] fix: Issue 4430 user wants to use one key vault for encryption (#4453) ## Description User wants to use one key vault to have managedService and manadgeDisk for databricks workspace encryption. Changes are in the key vault reference in main.bicep and in the workspace creation to put in "if" logic so that if a user uses the same key vault to pass in to the module like this (note same key vault in both calls) `customerManagedKey: { keyName: nestedDependencies.outputs.keyVaultKeyName keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId } customerManagedKeyManagedDisk: { keyName: nestedDependencies.outputs.keyVaultKeyName keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId autoRotationEnabled: false }` INSTEAD OF THE DEFAULT (using two key vaults) ` customerManagedKey: { keyName: nestedDependencies.outputs.keyVaultKeyName keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId } customerManagedKeyManagedDisk: { keyName: nestedDependencies.outputs.keyVaultDiskKeyName keyVaultResourceId: nestedDependencies.outputs.keyVaultDiskResourceId autoRotationEnabled: false }` Closes #4430 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.databricks.workspace](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=4430-kvoneissue%23)](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --------- Co-authored-by: Alexander Sehr <ASehr@hotmail.de> --- avm/res/databricks/workspace/main.bicep | 16 +++++++++++----- avm/res/databricks/workspace/main.json | 8 ++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index fb1eadf689..275bfc842a 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -204,7 +204,8 @@ resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empt } } -resource cMKManagedDiskKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId)) { +// Added condition if the key vault for the managed disk is the same as for the default encryption. Without the condition, the same key vault would be defined twice in the same template, which is not allowed +resource cMKManagedDiskKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId) && customerManagedKeyManagedDisk.?keyVaultResourceId != customerManagedKey.?keyVaultResourceId) { name: last(split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? 'dummyVault'), '/')) scope: resourceGroup( split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? '//'), '/')[2], @@ -336,11 +337,16 @@ resource workspace 'Microsoft.Databricks/workspaces@2024-05-01' = { ? { keySource: 'Microsoft.Keyvault' keyVaultProperties: { - keyVaultUri: cMKManagedDiskKeyVault.properties.vaultUri + keyVaultUri: (customerManagedKeyManagedDisk!.?keyVaultName != customerManagedKey!.?keyVaultName) + ? cMKManagedDiskKeyVault.properties.vaultUri + : cMKKeyVault.properties.vaultUri keyName: customerManagedKeyManagedDisk!.keyName - keyVersion: !empty(customerManagedKeyManagedDisk.?keyVersion ?? '') - ? customerManagedKeyManagedDisk!.?keyVersion! - : last(split(cMKManagedDiskKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + keyVersion: last(split( + (customerManagedKeyManagedDisk!.?keyVaultName != customerManagedKey!.?keyVaultName) + ? cMKManagedDiskKeyVault::cMKKey.properties.keyUriWithVersion + : cMKKeyVault::cMKKey.properties.keyUriWithVersion, + '/' + )) } rotationToLatestKeyVersionEnabled: (customerManagedKeyManagedDisk.?autoRotationEnabled ?? true == true) ?? false } diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index 01d52c2168..e309e53c9d 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "5273652821148246861" + "templateHash": "18270590516761452101" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace." @@ -954,7 +954,7 @@ "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" }, "cMKManagedDiskKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName')))))]", + "condition": "[and(and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))), and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", @@ -992,7 +992,7 @@ "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" }, "cMKManagedDiskKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId')))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))))]", "existing": true, "type": "Microsoft.KeyVault/vaults", "apiVersion": "2023-07-01", @@ -1009,7 +1009,7 @@ "sku": { "name": "[parameters('skuName')]" }, - "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), equals(true(), true())), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", + "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault').vaultUri, reference('cMKKeyVault').vaultUri), 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', last(split(if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, reference('cMKKeyVault::cMKKey').keyUriWithVersion), '/'))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), equals(true(), true())), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", "dependsOn": [ "cMKKeyVault::cMKKey", "cMKManagedDiskKeyVault::cMKKey", From 7fac3001fb455c6abea8abc41edd6e7e6ca582bb Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Mon, 17 Feb 2025 11:38:45 +0200 Subject: [PATCH 37/53] feat: add support for removing PIM role assignments in resource removal script (#4457) ## Description Adding resource removal logic for PIM role assignments ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [X] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- .../helper/Invoke-ResourceRemoval.ps1 | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 b/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 index 758d270459..384132e343 100644 --- a/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 +++ b/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 @@ -105,6 +105,55 @@ function Invoke-ResourceRemoval { $null = $roleAssignmentsOnScope | Where-Object { $_.RoleAssignmentId -eq $ResourceId } | Remove-AzRoleAssignment break } + 'Microsoft.Authorization/roleEligibilityScheduleRequests' { + $idElem = $ResourceId.Split('/') + $scope = $idElem[0..($idElem.Count - 5)] -join '/' + $pimRequestName = $idElem[-1] + $pimRoleAssignment = Get-AzRoleEligibilityScheduleRequest -Scope $scope -Name $pimRequestName + if ($pimRoleAssignment) { + $pimRoleAssignmentPrinicpalId = $pimRoleAssignment.PrincipalId + $pimRoleAssignmentRoleDefinitionId = $pimRoleAssignment.RoleDefinitionId + $guid = New-Guid + # PIM role assignments cannot be removed before 5 minutes from being created. Waiting for 5 minutes + Write-Verbose 'Waiting for 5 minutes before removing PIM role assignment' -Verbose + Start-Sleep -Seconds 300 + # The PIM ARM API doesn't support DELETE requests so the only way to delete an assignment is by creating a new assignment with `AdminRemove` type using a new GUID + $removalInputObject = @{ + Name = $guid + Scope = $scope + PrincipalId = $pimRoleAssignmentPrinicpalId + RequestType = 'AdminRemove' + RoleDefinitionId = $pimRoleAssignmentRoleDefinitionId + } + $null = New-AzRoleEligibilityScheduleRequest @removalInputObject + + } + break + } + 'Microsoft.Authorization/roleAssignmentScheduleRequests' { + $idElem = $ResourceId.Split('/') + $scope = $idElem[0..($idElem.Count - 5)] -join '/' + $pimRequestName = $idElem[-1] + $pimRoleAssignment = Get-AzRoleAssignmentScheduleRequest -Scope $scope -Name $pimRequestName + if ($pimRoleAssignment) { + $pimRoleAssignmentPrinicpalId = $pimRoleAssignment.PrincipalId + $pimRoleAssignmentRoleDefinitionId = $pimRoleAssignment.RoleDefinitionId + $guid = New-Guid + # PIM role assignments cannot be removed before 5 minutes from being created. Waiting for 5 minutes + Write-Verbose 'Waiting for 5 minutes before removing PIM role assignment' -Verbose + Start-Sleep -Seconds 300 + # The PIM ARM API doesn't support DELETE requests so the only way to delete an assignment is by creating a new assignment with `AdminRemove` type using a new GUID + $removalInputObject = @{ + Name = $guid + Scope = $scope + PrincipalId = $pimRoleAssignmentPrinicpalId + RequestType = 'AdminRemove' + RoleDefinitionId = $pimRoleAssignmentRoleDefinitionId + } + $null = New-AzRoleAssignmentScheduleRequest @removalInputObject + } + break + } 'Microsoft.RecoveryServices/vaults' { # Pre-Removal # ----------- From 188d6e2983541e153dff9e49d22b8128081d7804 Mon Sep 17 00:00:00 2001 From: Cosmin <13046837+C0smin@users.noreply.github.com> Date: Mon, 17 Feb 2025 12:25:31 +0100 Subject: [PATCH 38/53] fix: Removes typo secrect (#4441) ## Description Updates module to fix typo ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting 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`. - [x] 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 Co-authored-by: Guillaume Beaud <79973892+gbeaud@users.noreply.github.com> --- avm/res/cdn/profile/customdomain/main.bicep | 4 +-- avm/res/cdn/profile/customdomain/main.json | 4 +-- avm/res/cdn/profile/main.bicep | 2 +- avm/res/cdn/profile/main.json | 28 +++++++++---------- avm/res/cdn/profile/secret/README.md | 20 ++++++------- avm/res/cdn/profile/secret/main.bicep | 12 ++++---- avm/res/cdn/profile/secret/main.json | 14 +++++----- .../cdn/profile/securityPolicies/README.md | 4 +-- .../cdn/profile/securityPolicies/main.bicep | 4 +-- .../cdn/profile/securityPolicies/main.json | 6 ++-- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/avm/res/cdn/profile/customdomain/main.bicep b/avm/res/cdn/profile/customdomain/main.bicep index 6633912457..ed772b9f8f 100644 --- a/avm/res/cdn/profile/customdomain/main.bicep +++ b/avm/res/cdn/profile/customdomain/main.bicep @@ -40,7 +40,7 @@ param secretName string = '' resource profile 'Microsoft.Cdn/profiles@2023-05-01' existing = { name: profileName - resource secrect 'secrets@2023-05-01' existing = if (!empty(secretName)) { + resource secret 'secrets@2023-05-01' existing = if (!empty(secretName)) { name: secretName } } @@ -66,7 +66,7 @@ resource customDomain 'Microsoft.Cdn/profiles/customDomains@2023-05-01' = { minimumTlsVersion: minimumTlsVersion secret: !(empty(secretName)) ? { - id: profile::secrect.id + id: profile::secret.id } : null } diff --git a/avm/res/cdn/profile/customdomain/main.json b/avm/res/cdn/profile/customdomain/main.json index e01089b617..d27c7fd2d6 100644 --- a/avm/res/cdn/profile/customdomain/main.json +++ b/avm/res/cdn/profile/customdomain/main.json @@ -180,7 +180,7 @@ } }, "resources": { - "profile::secrect": { + "profile::secret": { "condition": "[not(empty(parameters('secretName')))]", "existing": true, "type": "Microsoft.Cdn/profiles/secrets", @@ -244,4 +244,4 @@ } } } -} \ No newline at end of file +} diff --git a/avm/res/cdn/profile/main.bicep b/avm/res/cdn/profile/main.bicep index a7fc53c7dc..9af67429cb 100644 --- a/avm/res/cdn/profile/main.bicep +++ b/avm/res/cdn/profile/main.bicep @@ -276,7 +276,7 @@ module profile_ruleSets 'ruleset/main.bicep' = [ params: { name: ruleSet.name profileName: profile.name - rules: ruleSet.rules + rules: ruleSet.?rules } } ] diff --git a/avm/res/cdn/profile/main.json b/avm/res/cdn/profile/main.json index 6f4275f270..fe76208a35 100644 --- a/avm/res/cdn/profile/main.json +++ b/avm/res/cdn/profile/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "2568064534163012915" + "templateHash": "17701317715444968473" }, "name": "CDN Profiles", "description": "This module deploys a CDN Profile." @@ -1693,7 +1693,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "6185880578813937844" + "templateHash": "14557033028846709449" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret." @@ -1702,7 +1702,7 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the secrect." + "description": "Required. The name of the secret." } }, "profileName": { @@ -1721,7 +1721,7 @@ "UrlSigningKey" ], "metadata": { - "description": "Optional. The type of the secrect." + "description": "Optional. The type of the secret." } }, "secretSourceResourceId": { @@ -1742,14 +1742,14 @@ "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. The subject alternative names of the secrect." + "description": "Optional. The subject alternative names of the secret." } }, "useLatestVersion": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Indicates whether to use the latest version of the secrect." + "description": "Optional. Indicates whether to use the latest version of the secret." } } }, @@ -1767,14 +1767,14 @@ "name": { "type": "string", "metadata": { - "description": "The name of the secrect." + "description": "The name of the secret." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the secrect." + "description": "The resource ID of the secret." }, "value": "[resourceId('Microsoft.Cdn/profiles/secrets', parameters('profileName'), parameters('name'))]" }, @@ -1842,7 +1842,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "13058047826352389276" + "templateHash": "10369216656034565871" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains." @@ -2016,7 +2016,7 @@ } }, "resources": { - "profile::secrect": { + "profile::secret": { "condition": "[not(empty(parameters('secretName')))]", "existing": true, "type": "Microsoft.Cdn/profiles/secrets", @@ -2764,7 +2764,7 @@ "value": "[parameters('name')]" }, "rules": { - "value": "[parameters('ruleSets')[copyIndex()].rules]" + "value": "[tryGet(parameters('ruleSets')[copyIndex()], 'rules')]" } }, "template": { @@ -4014,7 +4014,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "9521724516599685420" + "templateHash": "6987560302604046970" }, "name": "CDN Profiles Security Policy", "description": "This module deploys a CDN Profile Security Policy." @@ -4110,14 +4110,14 @@ "name": { "type": "string", "metadata": { - "description": "The name of the secrect." + "description": "The name of the secret." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the secrect." + "description": "The resource ID of the secret." }, "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" }, diff --git a/avm/res/cdn/profile/secret/README.md b/avm/res/cdn/profile/secret/README.md index b3048b8a2a..1a509353c3 100644 --- a/avm/res/cdn/profile/secret/README.md +++ b/avm/res/cdn/profile/secret/README.md @@ -20,7 +20,7 @@ This module deploys a CDN Profile Secret. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`name`](#parameter-name) | string | The name of the secrect. | +| [`name`](#parameter-name) | string | The name of the secret. | **Conditional parameters** @@ -34,13 +34,13 @@ This module deploys a CDN Profile Secret. | Parameter | Type | Description | | :-- | :-- | :-- | | [`secretVersion`](#parameter-secretversion) | string | The version of the secret. | -| [`subjectAlternativeNames`](#parameter-subjectalternativenames) | array | The subject alternative names of the secrect. | -| [`type`](#parameter-type) | string | The type of the secrect. | -| [`useLatestVersion`](#parameter-uselatestversion) | bool | Indicates whether to use the latest version of the secrect. | +| [`subjectAlternativeNames`](#parameter-subjectalternativenames) | array | The subject alternative names of the secret. | +| [`type`](#parameter-type) | string | The type of the secret. | +| [`useLatestVersion`](#parameter-uselatestversion) | bool | Indicates whether to use the latest version of the secret. | ### Parameter: `name` -The name of the secrect. +The name of the secret. - Required: Yes - Type: string @@ -70,7 +70,7 @@ The version of the secret. ### Parameter: `subjectAlternativeNames` -The subject alternative names of the secrect. +The subject alternative names of the secret. - Required: No - Type: array @@ -78,7 +78,7 @@ The subject alternative names of the secrect. ### Parameter: `type` -The type of the secrect. +The type of the secret. - Required: No - Type: string @@ -95,7 +95,7 @@ The type of the secrect. ### Parameter: `useLatestVersion` -Indicates whether to use the latest version of the secrect. +Indicates whether to use the latest version of the secret. - Required: No - Type: bool @@ -105,6 +105,6 @@ Indicates whether to use the latest version of the secrect. | Output | Type | Description | | :-- | :-- | :-- | -| `name` | string | The name of the secrect. | +| `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 secrect. | +| `resourceId` | string | The resource ID of the secret. | diff --git a/avm/res/cdn/profile/secret/main.bicep b/avm/res/cdn/profile/secret/main.bicep index e9c9eb7a67..9226638b10 100644 --- a/avm/res/cdn/profile/secret/main.bicep +++ b/avm/res/cdn/profile/secret/main.bicep @@ -1,7 +1,7 @@ metadata name = 'CDN Profiles Secret' metadata description = 'This module deploys a CDN Profile Secret.' -@description('Required. The name of the secrect.') +@description('Required. The name of the secret.') param name string @description('Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment.') @@ -13,7 +13,7 @@ param profileName string 'ManagedCertificate' 'UrlSigningKey' ]) -@description('Optional. The type of the secrect.') +@description('Optional. The type of the secret.') param type string = 'AzureFirstPartyManagedCertificate' @description('Conditional. The resource ID of the secret source. Required if the `type` is "CustomerCertificate".') @@ -23,10 +23,10 @@ param secretSourceResourceId string = '' @description('Optional. The version of the secret.') param secretVersion string = '' -@description('Optional. The subject alternative names of the secrect.') +@description('Optional. The subject alternative names of the secret.') param subjectAlternativeNames array = [] -@description('Optional. Indicates whether to use the latest version of the secrect.') +@description('Optional. Indicates whether to use the latest version of the secret.') param useLatestVersion bool = false resource profile 'Microsoft.Cdn/profiles@2023-05-01' existing = { @@ -53,10 +53,10 @@ resource secret 'Microsoft.Cdn/profiles/secrets@2023-05-01' = { } } -@description('The name of the secrect.') +@description('The name of the secret.') output name string = secret.name -@description('The resource ID of the secrect.') +@description('The resource ID of the secret.') output resourceId string = secret.id @description('The name of the resource group the secret was created in.') diff --git a/avm/res/cdn/profile/secret/main.json b/avm/res/cdn/profile/secret/main.json index 15711095a9..be33cf223e 100644 --- a/avm/res/cdn/profile/secret/main.json +++ b/avm/res/cdn/profile/secret/main.json @@ -14,7 +14,7 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the secrect." + "description": "Required. The name of the secret." } }, "profileName": { @@ -33,7 +33,7 @@ "UrlSigningKey" ], "metadata": { - "description": "Optional. The type of the secrect." + "description": "Optional. The type of the secret." } }, "secretSourceResourceId": { @@ -54,14 +54,14 @@ "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. The subject alternative names of the secrect." + "description": "Optional. The subject alternative names of the secret." } }, "useLatestVersion": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Indicates whether to use the latest version of the secrect." + "description": "Optional. Indicates whether to use the latest version of the secret." } } }, @@ -79,14 +79,14 @@ "name": { "type": "string", "metadata": { - "description": "The name of the secrect." + "description": "The name of the secret." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the secrect." + "description": "The resource ID of the secret." }, "value": "[resourceId('Microsoft.Cdn/profiles/secrets', parameters('profileName'), parameters('name'))]" }, @@ -98,4 +98,4 @@ "value": "[resourceGroup().name]" } } -} \ No newline at end of file +} diff --git a/avm/res/cdn/profile/securityPolicies/README.md b/avm/res/cdn/profile/securityPolicies/README.md index d82337c321..5370b79858 100644 --- a/avm/res/cdn/profile/securityPolicies/README.md +++ b/avm/res/cdn/profile/securityPolicies/README.md @@ -96,6 +96,6 @@ The name of the parent CDN profile. Required if the template is used in a standa | Output | Type | Description | | :-- | :-- | :-- | -| `name` | string | The name of the secrect. | +| `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 secrect. | +| `resourceId` | string | The resource ID of the secret. | diff --git a/avm/res/cdn/profile/securityPolicies/main.bicep b/avm/res/cdn/profile/securityPolicies/main.bicep index 5c08bb840b..bc96175787 100644 --- a/avm/res/cdn/profile/securityPolicies/main.bicep +++ b/avm/res/cdn/profile/securityPolicies/main.bicep @@ -43,10 +43,10 @@ type associationsType = { patternsToMatch: string[] }[] -@description('The name of the secrect.') +@description('The name of the secret.') output name string = securityPolicies.name -@description('The resource ID of the secrect.') +@description('The resource ID of the secret.') output resourceId string = securityPolicies.id @description('The name of the resource group the secret was created in.') diff --git a/avm/res/cdn/profile/securityPolicies/main.json b/avm/res/cdn/profile/securityPolicies/main.json index ba9e21e315..1e7e86ef6b 100644 --- a/avm/res/cdn/profile/securityPolicies/main.json +++ b/avm/res/cdn/profile/securityPolicies/main.json @@ -102,14 +102,14 @@ "name": { "type": "string", "metadata": { - "description": "The name of the secrect." + "description": "The name of the secret." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the secrect." + "description": "The resource ID of the secret." }, "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" }, @@ -121,4 +121,4 @@ "value": "[resourceGroup().name]" } } -} \ No newline at end of file +} From 230580af8a572e5310be7b9bf32f3871c5103e47 Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Mon, 17 Feb 2025 13:54:21 +0100 Subject: [PATCH 39/53] fix: Databricks - Applied workaround & module changes to enable publishing (#4459) ## Description - Change name of max test resource to temporarily fix an issue storage credentials - Implemented a module change in alignment with https://github.com/Azure/Azure-Verified-Modules/pull/1858 Closes #4430 Closes #4455 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.databricks.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2FdatabricksWorkaroundAgain&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 --- avm/res/databricks/workspace/README.md | 12 ++++---- avm/res/databricks/workspace/main.bicep | 16 +++++------ avm/res/databricks/workspace/main.json | 28 +++++++++---------- .../workspace/tests/e2e/max/main.test.bicep | 2 +- .../tests/e2e/waf-aligned/main.test.bicep | 2 +- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/avm/res/databricks/workspace/README.md b/avm/res/databricks/workspace/README.md index 857acbed83..c55f0dd197 100644 --- a/avm/res/databricks/workspace/README.md +++ b/avm/res/databricks/workspace/README.md @@ -102,7 +102,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { name: 'workspaceDeployment' params: { // Required parameters - name: 'dwmax003' + name: 'dwmax005' // Non-required parameters amlWorkspaceResourceId: '<amlWorkspaceResourceId>' automaticClusterUpdate: 'Enabled' @@ -233,7 +233,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { "parameters": { // Required parameters "name": { - "value": "dwmax003" + "value": "dwmax005" }, // Non-required parameters "amlWorkspaceResourceId": { @@ -424,7 +424,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { using 'br/public:avm/res/databricks/workspace:<version>' // Required parameters -param name = 'dwmax003' +param name = 'dwmax005' // Non-required parameters param amlWorkspaceResourceId = '<amlWorkspaceResourceId>' param automaticClusterUpdate = 'Enabled' @@ -556,7 +556,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { name: 'workspaceDeployment' params: { // Required parameters - name: 'dwwaf002' + name: 'dwwaf003' // Non-required parameters accessConnectorResourceId: '<accessConnectorResourceId>' amlWorkspaceResourceId: '<amlWorkspaceResourceId>' @@ -663,7 +663,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { "parameters": { // Required parameters "name": { - "value": "dwwaf002" + "value": "dwwaf003" }, // Non-required parameters "accessConnectorResourceId": { @@ -826,7 +826,7 @@ module workspace 'br/public:avm/res/databricks/workspace:<version>' = { using 'br/public:avm/res/databricks/workspace:<version>' // Required parameters -param name = 'dwwaf002' +param name = 'dwwaf003' // Non-required parameters param accessConnectorResourceId = '<accessConnectorResourceId>' param amlWorkspaceResourceId = '<amlWorkspaceResourceId>' diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index 275bfc842a..24c2ead5d3 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -193,10 +193,10 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { - name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + name: last(split(customerManagedKey.?keyVaultResourceId!, '/')) scope: resourceGroup( - split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], - split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4] + split(customerManagedKey.?keyVaultResourceId!, '/')[2], + split(customerManagedKey.?keyVaultResourceId!, '/')[4] ) resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { @@ -206,10 +206,10 @@ resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empt // Added condition if the key vault for the managed disk is the same as for the default encryption. Without the condition, the same key vault would be defined twice in the same template, which is not allowed resource cMKManagedDiskKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId) && customerManagedKeyManagedDisk.?keyVaultResourceId != customerManagedKey.?keyVaultResourceId) { - name: last(split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? 'dummyVault'), '/')) + name: last(split(customerManagedKeyManagedDisk.?keyVaultResourceId!, '/')) scope: resourceGroup( - split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? '//'), '/')[2], - split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? '////'), '/')[4] + split(customerManagedKeyManagedDisk.?keyVaultResourceId!, '/')[2], + split(customerManagedKeyManagedDisk.?keyVaultResourceId!, '/')[4] ) resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId) && !empty(customerManagedKeyManagedDisk.?keyName)) { @@ -327,7 +327,7 @@ resource workspace 'Microsoft.Databricks/workspaces@2024-05-01' = { keyVaultProperties: { keyVaultUri: cMKKeyVault.properties.vaultUri keyName: customerManagedKey!.keyName - keyVersion: !empty(customerManagedKey.?keyVersion ?? '') + keyVersion: !empty(customerManagedKey.?keyVersion) ? customerManagedKey!.?keyVersion! : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } @@ -348,7 +348,7 @@ resource workspace 'Microsoft.Databricks/workspaces@2024-05-01' = { '/' )) } - rotationToLatestKeyVersionEnabled: (customerManagedKeyManagedDisk.?autoRotationEnabled ?? true == true) ?? false + rotationToLatestKeyVersionEnabled: (customerManagedKeyManagedDisk.?autoRotationEnabled ?? true) ?? false } : null } diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index e309e53c9d..397570de24 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "18270590516761452101" + "templateHash": "5660535001885352949" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace." @@ -949,18 +949,18 @@ "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'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" }, "cMKManagedDiskKeyVault::cMKKey": { "condition": "[and(and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))), and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName'), 'dummyKey'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/')), coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName'), 'dummyKey'))]" }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", @@ -987,18 +987,18 @@ "existing": true, "type": "Microsoft.KeyVault/vaults", "apiVersion": "2023-07-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'), '/'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" }, "cMKManagedDiskKeyVault": { "condition": "[and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))))]", "existing": true, "type": "Microsoft.KeyVault/vaults", "apiVersion": "2023-07-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '/'))]" }, "workspace": { "type": "Microsoft.Databricks/workspaces", @@ -1009,7 +1009,7 @@ "sku": { "name": "[parameters('skuName')]" }, - "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault').vaultUri, reference('cMKKeyVault').vaultUri), 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', last(split(if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, reference('cMKKeyVault::cMKKey').keyUriWithVersion), '/'))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), equals(true(), true())), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", + "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault').vaultUri, reference('cMKKeyVault').vaultUri), 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', last(split(if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, reference('cMKKeyVault::cMKKey').keyUriWithVersion), '/'))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), true()), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", "dependsOn": [ "cMKKeyVault::cMKKey", "cMKManagedDiskKeyVault::cMKKey", diff --git a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep index 3647c97b80..2e0bf93dcb 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep @@ -82,7 +82,7 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - name: '${namePrefix}${serviceShort}003' + name: '${namePrefix}${serviceShort}005' location: resourceLocation diagnosticSettings: [ { diff --git a/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep index 34cc2a2553..5221a10908 100644 --- a/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -83,7 +83,7 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - name: '${namePrefix}${serviceShort}002' + name: '${namePrefix}${serviceShort}003' diagnosticSettings: [ { name: 'customSetting' From 5fe1f37a0d33b00ca142ac496401d15a3a90f783 Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Mon, 17 Feb 2025 14:01:21 +0100 Subject: [PATCH 40/53] fix: `Set-ModuleReadme` fixed issue with too vague regex check (#4458) ## Description - The updated regex incorrectly considered a value like `abc1` as a primitive value. The updated format ensures, that no characters may preceed a number value - avoiding this error. - Updated module test file that implemented a workaround to cope with this issue - Re-ran readme generation across all modules with (as expected) not changes nor errors ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.batch.batch-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml/badge.svg?branch=users%2Falsehr%2FreadmePrimitiveRegexFix&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 --- avm/res/api-management/service/tests/e2e/max/main.test.bicep | 2 +- .../tests/e2e/waf-aligned/main.test.bicep | 4 ++-- utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/res/api-management/service/tests/e2e/max/main.test.bicep b/avm/res/api-management/service/tests/e2e/max/main.test.bicep index 066da61641..e8d162bf6b 100644 --- a/avm/res/api-management/service/tests/e2e/max/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/max/main.test.bicep @@ -82,7 +82,7 @@ module testDeployment '../../../main.bicep' = [ publisherName: '${namePrefix}-az-amorg-x-001' additionalLocations: [ { - location: '${locationRegion2}' + location: locationRegion2 sku: { name: 'Premium' capacity: 1 diff --git a/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep index fc60bfa3a7..1cec1b3b44 100644 --- a/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep @@ -110,7 +110,7 @@ module testDeployment '../../../main.bicep' = [ targetResourceId: nestedDependencies.outputs.webApp01ResourceId weight: 1 priority: 1 - endpointLocation: '${enforcedLocation01}' + endpointLocation: enforcedLocation01 endpointStatus: 'Enabled' } } @@ -121,7 +121,7 @@ module testDeployment '../../../main.bicep' = [ targetResourceId: nestedDependencies.outputs.webApp02ResourceId weight: 1 priority: 2 - endpointLocation: '${enforcedLocation02}' + endpointLocation: enforcedLocation02 endpointStatus: 'Enabled' } } diff --git a/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 index dd7ea68267..6d594c0a2d 100644 --- a/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 +++ b/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 @@ -1165,7 +1165,7 @@ function ConvertTo-FormattedJSONParameterObject { $isLineWithStringValue = $lineValue -match '^".+"$' # e.g. "value" $isLineWithFunction = $lineValue -match '^[a-zA-Z0-9]+\(.+' # e.g., split(something) or loadFileAsBase64("./test.pfx") $isLineWithPlainValue = $lineValue -match '^\w+$' # e.g. adminPassword: password - $isLineWithPrimitiveValue = $lineValue -match '^\s*true|false|[0-9]+$' # e.g., isSecure: true + $isLineWithPrimitiveValue = $lineValue -match '^\s*(true|false|[0-9])+$' # e.g., isSecure: true $isLineContainingCondition = $lineValue -match '^\w+ [=!?|&]{2} .+\?.+\:.+$' # e.g., iteration == "init" ? "A" : "B" # Special case: Multi-line function From 15d21a14515570422049a9d6b2309c880ea3052d Mon Sep 17 00:00:00 2001 From: Sujay Pillai <sujayopillai@gmail.com> Date: Tue, 18 Feb 2025 20:56:12 +0800 Subject: [PATCH 41/53] feat: Added Indonesia Central region to the list (#4429) --- avm/ptn/network/private-link-private-dns-zones/main.bicep | 2 ++ avm/ptn/network/private-link-private-dns-zones/main.json | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/avm/ptn/network/private-link-private-dns-zones/main.bicep b/avm/ptn/network/private-link-private-dns-zones/main.bicep index 03ae5e93ba..5b05cd09a6 100644 --- a/avm/ptn/network/private-link-private-dns-zones/main.bicep +++ b/avm/ptn/network/private-link-private-dns-zones/main.bicep @@ -187,6 +187,7 @@ var azureRegionGeoCodeShortNameAsKey = { usgovarizona: 'uga' usdodeast: 'ude' usdodcentral: 'udc' + indonesiacentral: 'idc' } var azureRegionShortNameDisplayNameAsKey = { @@ -252,6 +253,7 @@ var azureRegionShortNameDisplayNameAsKey = { 'usgov arizona': 'usgovarizona' 'usdod east': 'usdodeast' 'usdod central': 'usdodcentral' + 'indonesia central': 'indonesiacentral' } var locationLowered = toLower(location) diff --git a/avm/ptn/network/private-link-private-dns-zones/main.json b/avm/ptn/network/private-link-private-dns-zones/main.json index 5a223851e5..b81390d3e7 100644 --- a/avm/ptn/network/private-link-private-dns-zones/main.json +++ b/avm/ptn/network/private-link-private-dns-zones/main.json @@ -238,7 +238,8 @@ "usgovtexas": "ugt", "usgovarizona": "uga", "usdodeast": "ude", - "usdodcentral": "udc" + "usdodcentral": "udc", + "indonesiacentral" : "idc" }, "azureRegionShortNameDisplayNameAsKey": { "australia southeast": "australiasoutheast", @@ -302,7 +303,8 @@ "usgov texas": "usgovtexas", "usgov arizona": "usgovarizona", "usdod east": "usdodeast", - "usdod central": "usdodcentral" + "usdod central": "usdodcentral", + "indonesia central": "indonesiacentral" }, "locationLowered": "[toLower(parameters('location'))]", "locationLoweredAndSpacesRemoved": "[if(contains(variables('locationLowered'), ' '), variables('azureRegionShortNameDisplayNameAsKey')[variables('locationLowered')], variables('locationLowered'))]", From 3d1488032a8672c1c15b69fea2e0ed68daf68954 Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Tue, 18 Feb 2025 20:48:29 +0100 Subject: [PATCH 42/53] feat: Updated PE scope to create the PE next to the primary resource by default (#4449) ## Description Background: https://github.com/Azure/bicep-registry-modules/issues/3835#issuecomment-2658956734 Linked to: https://github.com/Azure/Azure-Verified-Modules/pull/1857 Changes the deployment so that the main resource's (e.g., Key Vaults) RG is used as the default location for the PE. The already implemented `resourceGroupResourceId` will continue to allow the user to specify a different RG (in a different subscription, if needed). The primary change is from ```bicep scope: resourceGroup( split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] ) ``` to ```bicep scope: resourceGroup( split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) ``` I'll quote from @ahmadabdalla on this matter > In most scenarios, PEs are deployed alongside their main resource in their own RG vs. the VNET RG. Customer app teams may have subnet join permissions on a centralised VNET in a Landing zone, but may not have permissions to deploy into it. Also considering billing and resource lifecycle perspective. cc: @JamesDawson Ref: #4449 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- avm/res/cache/redis/main.bicep | 16 +- avm/res/cache/redis/main.json | 18 +- avm/res/cache/redis/version.json | 4 +- .../container-registry/registry/main.bicep | 16 +- avm/res/container-registry/registry/main.json | 8 +- .../container-registry/registry/version.json | 4 +- avm/res/databricks/workspace/main.bicep | 34 +- avm/res/databricks/workspace/main.json | 16 +- avm/res/databricks/workspace/version.json | 2 +- .../flexible-server/main.bicep | 16 +- .../flexible-server/main.json | 8 +- .../flexible-server/version.json | 2 +- avm/res/event-grid/namespace/main.bicep | 16 +- avm/res/event-grid/namespace/main.json | 38 +- avm/res/event-grid/namespace/version.json | 4 +- .../insights/private-link-scope/main.bicep | 4 +- avm/res/insights/private-link-scope/main.json | 12 +- .../insights/private-link-scope/version.json | 10 +- avm/res/key-vault/vault/main.bicep | 4 +- avm/res/key-vault/vault/main.json | 6 +- avm/res/key-vault/vault/version.json | 12 +- avm/res/purview/account/main.bicep | 80 +- avm/res/purview/account/main.json | 32 +- avm/res/purview/account/version.json | 4 +- avm/res/recovery-services/vault/main.bicep | 17 +- avm/res/recovery-services/vault/main.json | 8 +- avm/res/recovery-services/vault/version.json | 4 +- avm/res/relay/namespace/main.bicep | 16 +- avm/res/relay/namespace/main.json | 8 +- avm/res/relay/namespace/version.json | 4 +- avm/res/service-bus/namespace/main.bicep | 18 +- avm/res/service-bus/namespace/main.json | 10 +- avm/res/service-bus/namespace/version.json | 4 +- avm/res/sql/server/main.bicep | 16 +- avm/res/sql/server/main.json | 8 +- avm/res/sql/server/version.json | 4 +- avm/res/storage/storage-account/main.bicep | 4 +- avm/res/storage/storage-account/main.json | 6 +- avm/res/storage/storage-account/version.json | 4 +- avm/res/synapse/workspace/README.md | 2 +- avm/res/synapse/workspace/main.bicep | 18 +- avm/res/synapse/workspace/main.json | 82 +- avm/res/synapse/workspace/version.json | 4 +- avm/res/web/site/README.md | 2 +- avm/res/web/site/main.bicep | 49 +- avm/res/web/site/main.json | 1148 +++++++++-------- avm/res/web/site/slot/README.md | 4 +- avm/res/web/site/slot/main.bicep | 47 +- avm/res/web/site/slot/main.json | 556 ++++---- avm/res/web/site/version.json | 12 +- avm/res/web/static-site/README.md | 4 +- avm/res/web/static-site/main.bicep | 47 +- avm/res/web/static-site/main.json | 556 ++++---- avm/res/web/static-site/version.json | 4 +- 54 files changed, 1644 insertions(+), 1388 deletions(-) diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index 6494bafab4..5fb715ddd1 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -305,8 +305,8 @@ module redis_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1 for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-redis-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(redis.id, '/'))}-${privateEndpoint.?service ?? 'redisCache'}-${index}' @@ -455,12 +455,12 @@ output location string = redis.location @description('The private endpoints of the Redis Cache.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: redis_privateEndpoints[i].outputs.name - resourceId: redis_privateEndpoints[i].outputs.resourceId - groupId: redis_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: redis_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: redis_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: redis_privateEndpoints[index].outputs.name + resourceId: redis_privateEndpoints[index].outputs.resourceId + groupId: redis_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: redis_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: redis_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index d63179ebfc..7fe701ef6a 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2636464673565813214" + "version": "0.33.13.18514", + "templateHash": "9499818712606838127" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache." @@ -1167,8 +1167,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-redis-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1947,8 +1947,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "322534394383651316" + "version": "0.33.13.18514", + "templateHash": "11294861621866290910" }, "name": "Redis Cache Linked Servers", "description": "This module connects a primary and secondary Redis Cache together for geo-replication." @@ -2073,8 +2073,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8063348652715653257" + "version": "0.33.13.18514", + "templateHash": "14045530027687796477" } }, "definitions": { @@ -2261,7 +2261,7 @@ "description": "The private endpoints of the Redis Cache." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/cache/redis/version.json b/avm/res/cache/redis/version.json index 23f3815885..7466cbe674 100644 --- a/avm/res/cache/redis/version.json +++ b/avm/res/cache/redis/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/container-registry/registry/main.bicep b/avm/res/container-registry/registry/main.bicep index f6a01e5d55..682774e4da 100644 --- a/avm/res/container-registry/registry/main.bicep +++ b/avm/res/container-registry/registry/main.bicep @@ -448,8 +448,8 @@ module registry_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.1 for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-registry-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(registry.id, '/'))}-${privateEndpoint.?service ?? 'registry'}-${index}' @@ -532,12 +532,12 @@ output credentialSetsResourceIds array = [ @description('The private endpoints of the Azure container registry.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: registry_privateEndpoints[i].outputs.name - resourceId: registry_privateEndpoints[i].outputs.resourceId - groupId: registry_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: registry_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: registry_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: registry_privateEndpoints[index].outputs.name + resourceId: registry_privateEndpoints[index].outputs.resourceId + groupId: registry_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: registry_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: registry_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json index c28fcccf4d..6506d73f14 100644 --- a/avm/res/container-registry/registry/main.json +++ b/avm/res/container-registry/registry/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "1879570214296822193" + "templateHash": "9895920276913879197" }, "name": "Azure Container Registries (ACR)", "description": "This module deploys an Azure Container Registry (ACR)." @@ -2175,8 +2175,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2999,7 +2999,7 @@ "description": "The private endpoints of the Azure container registry." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/container-registry/registry/version.json b/avm/res/container-registry/registry/version.json index 9a9a06e897..b39a201436 100644 --- a/avm/res/container-registry/registry/version.json +++ b/avm/res/container-registry/registry/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index 24c2ead5d3..1ba0d3ef7d 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -445,8 +445,8 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' @@ -509,8 +509,8 @@ module storageAccount_storageAccountPrivateEndpoints 'br/public:avm/res/network/ for (privateEndpoint, index) in (storageAccountPrivateEndpoints ?? []): if (privateStorageAccount == 'Enabled') { name: '${uniqueString(deployment().name, location)}-workspacestorage-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${_storageAccountName}-${privateEndpoint.service}-${index}' @@ -592,25 +592,23 @@ output workspaceResourceId string = workspace.properties.workspaceId @description('The private endpoints of the Databricks Workspace.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: workspace_privateEndpoints[i].outputs.name - resourceId: workspace_privateEndpoints[i].outputs.resourceId - groupId: workspace_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: workspace_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: workspace_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: workspace_privateEndpoints[index].outputs.name + resourceId: workspace_privateEndpoints[index].outputs.resourceId + groupId: workspace_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: workspace_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: workspace_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] @description('The private endpoints of the Databricks Workspace Storage.') output storagePrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in ((!empty(storageAccountPrivateEndpoints) && privateStorageAccount == 'Enabled') - ? array(storageAccountPrivateEndpoints) - : []): { - name: storageAccount_storageAccountPrivateEndpoints[i].outputs.name - resourceId: storageAccount_storageAccountPrivateEndpoints[i].outputs.resourceId - groupId: storageAccount_storageAccountPrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: storageAccount_storageAccountPrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: storageAccount_storageAccountPrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateStorageAccount == 'Enabled' ? storageAccountPrivateEndpoints ?? [] : []): { + name: storageAccount_storageAccountPrivateEndpoints[index].outputs.name + resourceId: storageAccount_storageAccountPrivateEndpoints[index].outputs.resourceId + groupId: storageAccount_storageAccountPrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: storageAccount_storageAccountPrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: storageAccount_storageAccountPrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index 397570de24..7667b60709 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "5660535001885352949" + "templateHash": "11877768173688066898" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace." @@ -1011,8 +1011,8 @@ }, "properties": "[shallowMerge(createArray(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', shallowMerge(createArray(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault').vaultUri, reference('cMKKeyVault').vaultUri), 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', last(split(if(not(equals(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultName'), tryGet(parameters('customerManagedKey'), 'keyVaultName'))), reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, reference('cMKKeyVault::cMKKey').keyUriWithVersion), '/'))), 'rotationToLatestKeyVersionEnabled', coalesce(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'autoRotationEnabled'), true()), false())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()), if(or(or(not(empty(parameters('automaticClusterUpdate'))), not(empty(parameters('complianceStandards')))), not(empty(parameters('enhancedSecurityMonitoring')))), createObject('enhancedSecurityCompliance', createObject('automaticClusterUpdate', createObject('value', parameters('automaticClusterUpdate')), 'complianceSecurityProfile', createObject('complianceStandards', parameters('complianceStandards'), 'value', parameters('complianceSecurityProfileValue')), 'enhancedSecurityMonitoring', createObject('value', parameters('enhancedSecurityMonitoring')))), createObject())))]", "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKManagedDiskKeyVault::cMKKey", + "cMKKeyVault::cMKKey", "cMKKeyVault", "cMKManagedDiskKeyVault" ] @@ -1095,8 +1095,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1855,8 +1855,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspacestorage-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2685,7 +2685,7 @@ "description": "The private endpoints of the Databricks Workspace." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", @@ -2704,7 +2704,7 @@ "description": "The private endpoints of the Databricks Workspace Storage." }, "copy": { - "count": "[length(if(and(not(empty(parameters('storageAccountPrivateEndpoints'))), equals(parameters('privateStorageAccount'), 'Enabled')), array(parameters('storageAccountPrivateEndpoints')), createArray()))]", + "count": "[length(if(equals(parameters('privateStorageAccount'), 'Enabled'), coalesce(parameters('storageAccountPrivateEndpoints'), createArray()), createArray()))]", "input": { "name": "[reference(format('storageAccount_storageAccountPrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('storageAccount_storageAccountPrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/databricks/workspace/version.json b/avm/res/databricks/workspace/version.json index d96f771b50..fa995a3ef4 100644 --- a/avm/res/databricks/workspace/version.json +++ b/avm/res/databricks/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.10", + "version": "0.11", "pathFilters": [ "./main.json" ] diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.bicep b/avm/res/db-for-postgre-sql/flexible-server/main.bicep index dc508d19a4..86d932de42 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/main.bicep @@ -454,8 +454,8 @@ module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10. for (privateEndpoint, index) in (privateEndpoints ?? []): if (empty(delegatedSubnetResourceId)) { name: '${uniqueString(deployment().name, location)}-PostgreSQL-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(flexibleServer.id, '/'))}-${privateEndpoint.?service ?? 'postgresqlServer'}-${index}' @@ -522,12 +522,12 @@ output fqdn string = flexibleServer.properties.fullyQualifiedDomainName @description('The private endpoints of the PostgreSQL Flexible server.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: server_privateEndpoints[i].outputs.name - resourceId: server_privateEndpoints[i].outputs.resourceId - groupId: server_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: server_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: server_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: server_privateEndpoints[index].outputs.name + resourceId: server_privateEndpoints[index].outputs.resourceId + groupId: server_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: server_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: server_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.json b/avm/res/db-for-postgre-sql/flexible-server/main.json index 6f63a1e86e..40da17f7d9 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "17738367672833372176" + "templateHash": "8506855526660659778" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server." @@ -1731,8 +1731,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PostgreSQL-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2526,7 +2526,7 @@ "description": "The private endpoints of the PostgreSQL Flexible server." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/db-for-postgre-sql/flexible-server/version.json b/avm/res/db-for-postgre-sql/flexible-server/version.json index b39a201436..d96f771b50 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/version.json +++ b/avm/res/db-for-postgre-sql/flexible-server/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.9", + "version": "0.10", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/namespace/main.bicep b/avm/res/event-grid/namespace/main.bicep index 65518fdc16..856b561a0c 100644 --- a/avm/res/event-grid/namespace/main.bicep +++ b/avm/res/event-grid/namespace/main.bicep @@ -276,8 +276,8 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(namespace.id, '/'))}-${privateEndpoint.?service ?? 'topic'}-${index}' @@ -454,12 +454,12 @@ output topicResourceIds array = [ ] @description('The private endpoints of the EventGrid Namespace.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: namespace_privateEndpoints[i].outputs.name - resourceId: namespace_privateEndpoints[i].outputs.resourceId - groupId: namespace_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: namespace_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: namespace_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: namespace_privateEndpoints[index].outputs.name + resourceId: namespace_privateEndpoints[index].outputs.resourceId + groupId: namespace_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: namespace_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: namespace_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/event-grid/namespace/main.json b/avm/res/event-grid/namespace/main.json index 710d97699d..19ca5f738c 100644 --- a/avm/res/event-grid/namespace/main.json +++ b/avm/res/event-grid/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "4896325805194987111" + "version": "0.33.13.18514", + "templateHash": "1976622211901619784" }, "name": "Event Grid Namespaces", "description": "This module deploys an Event Grid Namespace." @@ -936,8 +936,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-namespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1728,8 +1728,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3750609028879158720" + "version": "0.33.13.18514", + "templateHash": "13625381498949263441" }, "name": "Eventgrid Namespace Topics", "description": "This module deploys an Eventgrid Namespace Topic." @@ -2022,8 +2022,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2786410706674700781" + "version": "0.33.13.18514", + "templateHash": "14274639062376243006" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription." @@ -2320,8 +2320,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5500161725034179010" + "version": "0.33.13.18514", + "templateHash": "16684000080098402376" }, "name": "Eventgrid Namespace CA Certificates", "description": "This module deploys an Eventgrid Namespace CA Certificate." @@ -2446,8 +2446,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "310861179374860968" + "version": "0.33.13.18514", + "templateHash": "11246395673519136496" }, "name": "Eventgrid Namespace Clients", "description": "This module deploys an Eventgrid Namespace Client." @@ -2608,8 +2608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "15034210943727152335" + "version": "0.33.13.18514", + "templateHash": "15188233906898213813" }, "name": "Eventgrid Namespace Client Groups", "description": "This module deploys an Eventgrid Namespace Client Group." @@ -2725,8 +2725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8680886384150003323" + "version": "0.33.13.18514", + "templateHash": "2849991174853656103" }, "name": "Eventgrid Namespace Topic Spaces", "description": "This module deploys an Eventgrid Namespace Topic Space." @@ -2979,8 +2979,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "17347618187908890991" + "version": "0.33.13.18514", + "templateHash": "2140320336515533966" }, "name": "Eventgrid Namespace Permissions Bindings", "description": "This module deploys an Eventgrid Namespace Permission Binding." @@ -3135,7 +3135,7 @@ "description": "The private endpoints of the EventGrid Namespace." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/event-grid/namespace/version.json b/avm/res/event-grid/namespace/version.json index 21226dd43f..a89e5c9d3c 100644 --- a/avm/res/event-grid/namespace/version.json +++ b/avm/res/event-grid/namespace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/insights/private-link-scope/main.bicep b/avm/res/insights/private-link-scope/main.bicep index 10acf70b51..c061541297 100644 --- a/avm/res/insights/private-link-scope/main.bicep +++ b/avm/res/insights/private-link-scope/main.bicep @@ -159,8 +159,8 @@ module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endp name: '${uniqueString(deployment().name, location)}-privateLinkScope-PrivateEndpoint-${index}' // use the subnet resource group if the resource group is not explicitly provided scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(privateLinkScope.id, '/'))}-${privateEndpoint.?service ?? 'azuremonitor'}-${index}' diff --git a/avm/res/insights/private-link-scope/main.json b/avm/res/insights/private-link-scope/main.json index 18e2c4753b..589aebaee6 100644 --- a/avm/res/insights/private-link-scope/main.json +++ b/avm/res/insights/private-link-scope/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "11329844199871738536" + "version": "0.33.13.18514", + "templateHash": "5613742865192153837" }, "name": "Azure Monitor Private Link Scopes", "description": "This module deploys an Azure Monitor Private Link Scope." @@ -718,8 +718,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "18319290317274254807" + "version": "0.33.13.18514", + "templateHash": "16373447809622934069" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource." @@ -793,8 +793,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-privateLinkScope-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/insights/private-link-scope/version.json b/avm/res/insights/private-link-scope/version.json index 9ed3662aba..a89e5c9d3c 100644 --- a/avm/res/insights/private-link-scope/version.json +++ b/avm/res/insights/private-link-scope/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", - "pathFilters": [ - "./main.json" - ] + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.7", + "pathFilters": [ + "./main.json" + ] } diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep index 3c0200eca1..1650de8ae7 100644 --- a/avm/res/key-vault/vault/main.bicep +++ b/avm/res/key-vault/vault/main.bicep @@ -308,8 +308,8 @@ module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.1 for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-keyVault-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(keyVault.id, '/'))}-${privateEndpoint.?service ?? 'vault'}-${index}' diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json index 451db99b7b..2281286ba8 100644 --- a/avm/res/key-vault/vault/main.json +++ b/avm/res/key-vault/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10562288197267069295" + "templateHash": "3889592928706338435" }, "name": "Key Vaults", "description": "This module deploys a Key Vault." @@ -2272,8 +2272,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-keyVault-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/key-vault/vault/version.json b/avm/res/key-vault/vault/version.json index 6a120cace8..15548e8d1a 100644 --- a/avm/res/key-vault/vault/version.json +++ b/avm/res/key-vault/vault/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.11", - "pathFilters": [ - "./main.json" - ] -} \ No newline at end of file + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.12", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/purview/account/main.bicep b/avm/res/purview/account/main.bicep index 678cd98182..0b77d542e6 100644 --- a/avm/res/purview/account/main.bicep +++ b/avm/res/purview/account/main.bicep @@ -191,8 +191,8 @@ module account_accountPrivateEndpoints 'br/public:avm/res/network/private-endpoi for (privateEndpoint, index) in (accountPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-account-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}' @@ -246,8 +246,8 @@ module account_portalPrivateEndpoints 'br/public:avm/res/network/private-endpoin for (privateEndpoint, index) in (portalPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-portal-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'portal'}-${index}' @@ -301,8 +301,8 @@ module account_storageBlobPrivateEndpoints 'br/public:avm/res/network/private-en for (privateEndpoint, index) in (storageBlobPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-blob-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'blob'}-${index}' @@ -356,8 +356,8 @@ module account_storageQueuePrivateEndpoints 'br/public:avm/res/network/private-e for (privateEndpoint, index) in (storageQueuePrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-queue-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'queue'}-${index}' @@ -411,8 +411,8 @@ module account_eventHubPrivateEndpoints 'br/public:avm/res/network/private-endpo for (privateEndpoint, index) in (eventHubPrivateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-eventHub-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -507,56 +507,56 @@ output systemAssignedMIPrincipalId string? = account.?identity.?principalId @description('The private endpoints of the Purview Account.') output accountPrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(accountPrivateEndpoints) ? array(accountPrivateEndpoints) : []): { - name: account_accountPrivateEndpoints[i].outputs.name - resourceId: account_accountPrivateEndpoints[i].outputs.resourceId - groupId: account_accountPrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: account_accountPrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: account_accountPrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (accountPrivateEndpoints ?? []): { + name: account_accountPrivateEndpoints[index].outputs.name + resourceId: account_accountPrivateEndpoints[index].outputs.resourceId + groupId: account_accountPrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: account_accountPrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: account_accountPrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] @description('The private endpoints of the Purview Account Portal.') output portalPrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(portalPrivateEndpoints) ? array(portalPrivateEndpoints) : []): { - name: account_portalPrivateEndpoints[i].outputs.name - resourceId: account_portalPrivateEndpoints[i].outputs.resourceId - groupId: account_portalPrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: account_portalPrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: account_portalPrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (portalPrivateEndpoints ?? []): { + name: account_portalPrivateEndpoints[index].outputs.name + resourceId: account_portalPrivateEndpoints[index].outputs.resourceId + groupId: account_portalPrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: account_portalPrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: account_portalPrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] @description('The private endpoints of the managed storage account blob service.') output storageBlobPrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(storageBlobPrivateEndpoints) ? array(storageBlobPrivateEndpoints) : []): { - name: account_storageBlobPrivateEndpoints[i].outputs.name - resourceId: account_storageBlobPrivateEndpoints[i].outputs.resourceId - groupId: account_storageBlobPrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: account_storageBlobPrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: account_storageBlobPrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (storageBlobPrivateEndpoints ?? []): { + name: account_storageBlobPrivateEndpoints[index].outputs.name + resourceId: account_storageBlobPrivateEndpoints[index].outputs.resourceId + groupId: account_storageBlobPrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: account_storageBlobPrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: account_storageBlobPrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] @description('The private endpoints of the managed storage account queue service.') output storageQueuePrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(storageQueuePrivateEndpoints) ? array(storageQueuePrivateEndpoints) : []): { - name: account_storageQueuePrivateEndpoints[i].outputs.name - resourceId: account_storageQueuePrivateEndpoints[i].outputs.resourceId - groupId: account_storageQueuePrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: account_storageQueuePrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: account_storageQueuePrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (storageQueuePrivateEndpoints ?? []): { + name: account_storageQueuePrivateEndpoints[index].outputs.name + resourceId: account_storageQueuePrivateEndpoints[index].outputs.resourceId + groupId: account_storageQueuePrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: account_storageQueuePrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: account_storageQueuePrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] @description('The private endpoints of the managed Event Hub Namespace.') output eventHubPrivateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(eventHubPrivateEndpoints) ? array(eventHubPrivateEndpoints) : []): { - name: account_eventHubPrivateEndpoints[i].outputs.name - resourceId: account_eventHubPrivateEndpoints[i].outputs.resourceId - groupId: account_eventHubPrivateEndpoints[i].outputs.?groupId! - customDnsConfigs: account_eventHubPrivateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: account_eventHubPrivateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (eventHubPrivateEndpoints ?? []): { + name: account_eventHubPrivateEndpoints[index].outputs.name + resourceId: account_eventHubPrivateEndpoints[index].outputs.resourceId + groupId: account_eventHubPrivateEndpoints[index].outputs.?groupId! + customDnsConfigs: account_eventHubPrivateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: account_eventHubPrivateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/purview/account/main.json b/avm/res/purview/account/main.json index 886766ba2f..816e454b55 100644 --- a/avm/res/purview/account/main.json +++ b/avm/res/purview/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "17913522692754850617" + "templateHash": "7064847066299467788" }, "name": "Purview Accounts", "description": "This module deploys a Purview Account." @@ -875,8 +875,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-account-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1632,8 +1632,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-portal-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2389,8 +2389,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-blob-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3146,8 +3146,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-queue-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3903,8 +3903,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-eventHub-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -4727,7 +4727,7 @@ "description": "The private endpoints of the Purview Account." }, "copy": { - "count": "[length(if(not(empty(parameters('accountPrivateEndpoints'))), array(parameters('accountPrivateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('accountPrivateEndpoints'), createArray()))]", "input": { "name": "[reference(format('account_accountPrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('account_accountPrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", @@ -4746,7 +4746,7 @@ "description": "The private endpoints of the Purview Account Portal." }, "copy": { - "count": "[length(if(not(empty(parameters('portalPrivateEndpoints'))), array(parameters('portalPrivateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('portalPrivateEndpoints'), createArray()))]", "input": { "name": "[reference(format('account_portalPrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('account_portalPrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", @@ -4765,7 +4765,7 @@ "description": "The private endpoints of the managed storage account blob service." }, "copy": { - "count": "[length(if(not(empty(parameters('storageBlobPrivateEndpoints'))), array(parameters('storageBlobPrivateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('storageBlobPrivateEndpoints'), createArray()))]", "input": { "name": "[reference(format('account_storageBlobPrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('account_storageBlobPrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", @@ -4784,7 +4784,7 @@ "description": "The private endpoints of the managed storage account queue service." }, "copy": { - "count": "[length(if(not(empty(parameters('storageQueuePrivateEndpoints'))), array(parameters('storageQueuePrivateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('storageQueuePrivateEndpoints'), createArray()))]", "input": { "name": "[reference(format('account_storageQueuePrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('account_storageQueuePrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", @@ -4803,7 +4803,7 @@ "description": "The private endpoints of the managed Event Hub Namespace." }, "copy": { - "count": "[length(if(not(empty(parameters('eventHubPrivateEndpoints'))), array(parameters('eventHubPrivateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('eventHubPrivateEndpoints'), createArray()))]", "input": { "name": "[reference(format('account_eventHubPrivateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('account_eventHubPrivateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/purview/account/version.json b/avm/res/purview/account/version.json index 9a9a06e897..b39a201436 100644 --- a/avm/res/purview/account/version.json +++ b/avm/res/purview/account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 888cf7054c..8873f03850 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -370,8 +370,8 @@ module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-rsv-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(rsv.id, '/'))}-${privateEndpoint.?service ?? 'AzureSiteRecovery'}-${index}' @@ -454,15 +454,14 @@ output location string = rsv.location @description('The private endpoints of the recovery services vault.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: rsv_privateEndpoints[i].outputs.name - resourceId: rsv_privateEndpoints[i].outputs.resourceId - groupId: rsv_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: rsv_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: rsv_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: rsv_privateEndpoints[index].outputs.name + resourceId: rsv_privateEndpoints[index].outputs.resourceId + groupId: rsv_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: rsv_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: rsv_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] - // =============== // // Definitions // // =============== // diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index 189515ddce..5a6c5abfec 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "16908039055526294885" + "templateHash": "15610768134592067919" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault." @@ -2953,8 +2953,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-rsv-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3749,7 +3749,7 @@ "description": "The private endpoints of the recovery services vault." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/recovery-services/vault/version.json b/avm/res/recovery-services/vault/version.json index 09c3664cec..b6668657e7 100644 --- a/avm/res/recovery-services/vault/version.json +++ b/avm/res/recovery-services/vault/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/relay/namespace/main.bicep b/avm/res/relay/namespace/main.bicep index 5de0130cc8..bbfac6b1b7 100644 --- a/avm/res/relay/namespace/main.bicep +++ b/avm/res/relay/namespace/main.bicep @@ -219,8 +219,8 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(namespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -300,12 +300,12 @@ output location string = namespace.location @description('The private endpoints of the relay namespace.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: namespace_privateEndpoints[i].outputs.name - resourceId: namespace_privateEndpoints[i].outputs.resourceId - groupId: namespace_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: namespace_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: namespace_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (pe, index) in (privateEndpoints ?? []): { + name: namespace_privateEndpoints[index].outputs.name + resourceId: namespace_privateEndpoints[index].outputs.resourceId + groupId: namespace_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: namespace_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: namespace_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/relay/namespace/main.json b/avm/res/relay/namespace/main.json index cbfc6ea08c..38cba7552c 100644 --- a/avm/res/relay/namespace/main.json +++ b/avm/res/relay/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "7449491400928242470" + "templateHash": "12918595360382784205" }, "name": "Relay Namespaces", "description": "This module deploys a Relay Namespace" @@ -1931,8 +1931,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-namespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2719,7 +2719,7 @@ "description": "The private endpoints of the relay namespace." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/relay/namespace/version.json b/avm/res/relay/namespace/version.json index 21226dd43f..a89e5c9d3c 100644 --- a/avm/res/relay/namespace/version.json +++ b/avm/res/relay/namespace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index ef6fcf6ca5..3c4c9e1c9e 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -225,7 +225,7 @@ resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview keyName: customerManagedKey!.keyName keyVaultUri: cMKKeyVault.properties.vaultUri keyVersion: !empty(customerManagedKey.?keyVersion ?? '') - ? customerManagedKey!.keyVersion + ? customerManagedKey!.?keyVersion : (customerManagedKey.?autoRotationEnabled ?? true) ? null : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) @@ -379,8 +379,8 @@ module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-e for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-serviceBusNamespace-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(serviceBusNamespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -467,12 +467,12 @@ output location string = serviceBusNamespace.location @description('The private endpoints of the service bus namespace.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: serviceBusNamespace_privateEndpoints[i].outputs.name - resourceId: serviceBusNamespace_privateEndpoints[i].outputs.resourceId - groupId: serviceBusNamespace_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: serviceBusNamespace_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: serviceBusNamespace_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (pe, index) in (privateEndpoints ?? []): { + name: serviceBusNamespace_privateEndpoints[index].outputs.name + resourceId: serviceBusNamespace_privateEndpoints[index].outputs.resourceId + groupId: serviceBusNamespace_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: serviceBusNamespace_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: serviceBusNamespace_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index 3f9fba0140..0ad4bee63d 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "7837803421760030856" + "templateHash": "7026797580840545876" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace." @@ -1751,7 +1751,7 @@ "zoneRedundant": "[parameters('zoneRedundant')]", "disableLocalAuth": "[parameters('disableLocalAuth')]", "premiumMessagingPartitions": "[if(equals(parameters('skuObject').name, 'Premium'), parameters('premiumMessagingPartitions'), 0)]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), null(), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]" + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), null(), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]" }, "dependsOn": [ "cMKKeyVault::cMKKey", @@ -4375,8 +4375,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-serviceBusNamespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -5171,7 +5171,7 @@ "description": "The private endpoints of the service bus namespace." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/service-bus/namespace/version.json b/avm/res/service-bus/namespace/version.json index 23f3815885..7466cbe674 100644 --- a/avm/res/service-bus/namespace/version.json +++ b/avm/res/service-bus/namespace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 0e9d3a62cb..bc14c218dc 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -344,8 +344,8 @@ module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10. for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-server-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(server.id, '/'))}-${privateEndpoint.?service ?? 'sqlServer'}-${index}' @@ -567,12 +567,12 @@ output exportedSecrets secretsOutputType = (secretsExportConfiguration != null) @description('The private endpoints of the SQL server.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: server_privateEndpoints[i].outputs.name - resourceId: server_privateEndpoints[i].outputs.resourceId - groupId: server_privateEndpoints[i].outputs.?groupId! - customDnsConfigs: server_privateEndpoints[i].outputs.customDnsConfigs - networkInterfaceResourceIds: server_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (pe, index) in (privateEndpoints ?? []): { + name: server_privateEndpoints[index].outputs.name + resourceId: server_privateEndpoints[index].outputs.resourceId + groupId: server_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: server_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: server_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index 070453a235..2d29ebc025 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "11930227683565093918" + "templateHash": "17739275737446378477" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server." @@ -3685,8 +3685,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-server-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -5972,7 +5972,7 @@ "description": "The private endpoints of the SQL server." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", diff --git a/avm/res/sql/server/version.json b/avm/res/sql/server/version.json index 23f3815885..7466cbe674 100644 --- a/avm/res/sql/server/version.json +++ b/avm/res/sql/server/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 66b9fc6703..c6a0d7b02d 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -503,8 +503,8 @@ module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoi for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-storageAccount-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 9f0968f7e3..cb5eefe1d9 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "7065290388008718363" + "templateHash": "383280615510920938" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -1451,8 +1451,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 8f0ecca899..77443210ac 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.17", + "version": "0.18", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index 90ff37be68..c046e8bd25 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -2229,7 +2229,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.9.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index 2bc090c5f4..f4ec3db290 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -349,12 +349,12 @@ module workspace_firewallRules 'firewall-rules/main.bicep' = [ ] // Endpoints -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.9.1' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' @@ -446,12 +446,12 @@ output location string = workspace.location @description('The private endpoints of the Synapse Workspace.') output privateEndpoints privateEndpointOutputType[] = [ - for (pe, i) in (privateEndpoints ?? []): { - name: workspace_privateEndpoints[i].outputs.name - resourceId: workspace_privateEndpoints[i].outputs.resourceId - groupId: workspace_privateEndpoints[i].outputs.groupId - customDnsConfigs: workspace_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceResourceIds: workspace_privateEndpoints[i].outputs.networkInterfaceResourceIds + for (item, index) in (privateEndpoints ?? []): { + name: workspace_privateEndpoints[index].outputs.name + resourceId: workspace_privateEndpoints[index].outputs.resourceId + groupId: workspace_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: workspace_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: workspace_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index c58cd50e2f..dbb867dd4f 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "4395970160208281653" + "templateHash": "11532981053254062807" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace." @@ -1554,8 +1554,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1608,12 +1608,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15164027369246485309" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -1680,50 +1679,6 @@ "__bicep_export!": true } }, - "manualPrivateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, "privateLinkServiceConnectionType": { "type": "object", "properties": { @@ -1818,7 +1773,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -1916,7 +1871,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -2012,11 +1967,11 @@ "manualPrivateLinkServiceConnections": { "type": "array", "items": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionType" + "$ref": "#/definitions/privateLinkServiceConnectionType" }, "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { @@ -2026,7 +1981,7 @@ }, "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -2063,7 +2018,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2169,12 +2124,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "14591244586837935522" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -2314,7 +2268,7 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { + "customDnsConfigs": { "type": "array", "items": { "$ref": "#/definitions/customDnsConfigType" @@ -2407,8 +2361,8 @@ "input": { "name": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfigs": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "groupId": "[tryGet(tryGet(reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", "networkInterfaceResourceIds": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } diff --git a/avm/res/synapse/workspace/version.json b/avm/res/synapse/workspace/version.json index a830c3d961..fa995a3ef4 100644 --- a/avm/res/synapse/workspace/version.json +++ b/avm/res/synapse/workspace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.10", + "version": "0.11", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index 8467c27a68..f7d7b2e497 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -4164,7 +4164,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Notes diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 8d62479680..a63db407cf 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -481,12 +481,12 @@ resource app_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01 } ] -module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ +module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-app-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites'}-${index}' @@ -556,7 +556,7 @@ output systemAssignedMIPrincipalId string? = app.?identity.?principalId @description('The principal ID of the system assigned identity of slots.') output slotSystemAssignedMIPrincipalIds string[] = [ - for (slot, index) in (slots ?? []): app_slots[index].outputs.systemAssignedMIPrincipalId ?? '' + for (slot, index) in (slots ?? []): app_slots[index].outputs.?systemAssignedMIPrincipalId ?? '' ] @description('The location the resource was deployed into.') @@ -569,13 +569,13 @@ output defaultHostname string = app.properties.defaultHostName output customDomainVerificationId string = app.properties.customDomainVerificationId @description('The private endpoints of the site.') -output privateEndpoints array = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: app_privateEndpoints[i].outputs.name - resourceId: app_privateEndpoints[i].outputs.resourceId - groupId: app_privateEndpoints[i].outputs.groupId - customDnsConfig: app_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: app_privateEndpoints[i].outputs.networkInterfaceIds +output privateEndpoints privateEndpointOutputType[] = [ + for (item, index) in (privateEndpoints ?? []): { + name: app_privateEndpoints[index].outputs.name + resourceId: app_privateEndpoints[index].outputs.resourceId + groupId: app_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: app_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: app_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] @@ -584,3 +584,30 @@ output slotPrivateEndpoints array = [for (slot, index) in (slots ?? []): app_slo @description('The outbound IP addresses of the app.') output outboundIpAddresses string = app.properties.outboundIpAddresses + +// ================ // +// Definitions // +// ================ // +@export() +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string + + @description('The resource ID of the private endpoint.') + resourceId: string + + @description('The group Id for the private endpoint Group.') + groupId: string? + + @description('The custom DNS configurations of the private endpoint.') + customDnsConfigs: { + @description('FQDN that resolves to private endpoint IP address.') + fqdn: string? + + @description('A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[] + + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index dec2e20b5f..16bce6b8b7 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -6,12 +6,75 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "1510038828157796019" + "templateHash": "6063412784919637403" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App." }, "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { @@ -1749,12 +1812,75 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "15803822027091526791" + "templateHash": "5676574800935579140" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot." }, "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { @@ -3379,8 +3505,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-slot-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3433,12 +3559,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -3460,299 +3585,266 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } - } - }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." - } - } - } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, - "lockType": { + "ipConfigurationType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Required. The name of the resource that is unique within a resource group." } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, + "type": "object", "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." } }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true - }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." } }, - "metadata": { - "description": "Required. Properties of private link service connection." + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, + "type": "object", "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } + "groupIds": { + "type": "array", + "items": { + "type": "string" }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." } }, - "metadata": { - "description": "Required. Properties of private link service connection." + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } + }, + "metadata": { + "description": "Required. Properties of private link service connection." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, - "privateDnsZoneGroupConfigType": { + "lockType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. Specify the name of lock." } }, - "privateDnsZoneResourceId": { + "kind": { "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Optional. Specify the type of lock." } } }, "metadata": { + "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } }, - "subnetResourceId": { - "type": "string", + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "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." } }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -3766,7 +3858,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -3787,12 +3883,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -3805,21 +3906,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -3848,7 +3961,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -3856,7 +3969,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3962,12 +4075,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -4045,10 +4157,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -4110,26 +4219,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -4178,17 +4294,20 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the slot." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } } @@ -4428,8 +4547,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-app-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -4482,12 +4601,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -4509,80 +4627,118 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "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\"." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "lockType": { "type": "object", @@ -4607,182 +4763,108 @@ } } }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } - }, - "nullable": true + } }, - "privateDnsZoneGroupConfigType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "privateDnsZoneResourceId": { + "roleDefinitionIdOrName": { "type": "string", "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, "metadata": { + "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -4802,6 +4884,9 @@ }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -4815,7 +4900,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -4836,12 +4925,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -4854,21 +4948,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -4897,7 +5003,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -4905,7 +5011,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -5011,12 +5117,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -5094,10 +5199,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -5159,26 +5261,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -5248,7 +5357,7 @@ }, "copy": { "count": "[length(coalesce(parameters('slots'), createArray()))]", - "input": "[coalesce(reference(format('app_slots[{0}]', copyIndex())).outputs.systemAssignedMIPrincipalId.value, '')]" + "input": "[coalesce(tryGet(tryGet(reference(format('app_slots[{0}]', copyIndex())).outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" } }, "location": { @@ -5274,17 +5383,20 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the site." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } }, diff --git a/avm/res/web/site/slot/README.md b/avm/res/web/site/slot/README.md index 765c727940..f9c155863a 100644 --- a/avm/res/web/site/slot/README.md +++ b/avm/res/web/site/slot/README.md @@ -816,7 +816,7 @@ Array of role assignments to create. - `'Owner'` - `'Private DNS Zone Contributor'` - `'Reader'` - - `'Role Based Access Control Administrator (Preview)'` + - `'Role Based Access Control Administrator'` **Required parameters** @@ -1158,7 +1158,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Notes diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index f41579ba90..fd21d9c7b0 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -374,12 +374,12 @@ resource slot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-0 } ] -module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ +module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-slot-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(app.id, '/'))}-${privateEndpoint.?service ?? 'sites-${slot.name}'}-${index}' @@ -445,12 +445,39 @@ output systemAssignedMIPrincipalId string? = slot.?identity.?principalId output location string = slot.location @description('The private endpoints of the slot.') -output privateEndpoints array = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: slot_privateEndpoints[i].outputs.name - resourceId: slot_privateEndpoints[i].outputs.resourceId - groupId: slot_privateEndpoints[i].outputs.groupId - customDnsConfig: slot_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: slot_privateEndpoints[i].outputs.networkInterfaceIds +output privateEndpoints privateEndpointOutputType[] = [ + for (item, index) in (privateEndpoints ?? []): { + name: slot_privateEndpoints[index].outputs.name + resourceId: slot_privateEndpoints[index].outputs.resourceId + groupId: slot_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: slot_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: slot_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] + +// ================ // +// Definitions // +// ================ // +@export() +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string + + @description('The resource ID of the private endpoint.') + resourceId: string + + @description('The group Id for the private endpoint Group.') + groupId: string? + + @description('The custom DNS configurations of the private endpoint.') + customDnsConfigs: { + @description('FQDN that resolves to private endpoint IP address.') + fqdn: string? + + @description('A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[] + + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index f7a5a18c95..6a27566930 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -6,12 +6,75 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "15803822027091526791" + "templateHash": "5676574800935579140" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot." }, "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { @@ -1636,8 +1699,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-slot-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1690,12 +1753,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -1717,80 +1779,118 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "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\"." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "lockType": { "type": "object", @@ -1815,182 +1915,108 @@ } } }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } - }, - "nullable": true + } }, - "privateDnsZoneGroupConfigType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "privateDnsZoneResourceId": { + "roleDefinitionIdOrName": { "type": "string", "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, "metadata": { + "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -2010,6 +2036,9 @@ }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -2023,7 +2052,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -2044,12 +2077,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -2062,21 +2100,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -2105,7 +2155,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -2113,7 +2163,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2219,12 +2269,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -2302,10 +2351,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -2367,26 +2413,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -2435,17 +2488,20 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the slot." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } } diff --git a/avm/res/web/site/version.json b/avm/res/web/site/version.json index ba8977caae..5c79d51eae 100644 --- a/avm/res/web/site/version.json +++ b/avm/res/web/site/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.13", - "pathFilters": [ - "./main.json" - ] -} \ No newline at end of file + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.14", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/web/static-site/README.md b/avm/res/web/static-site/README.md index 617c22a2d7..b4a95aa2d1 100644 --- a/avm/res/web/static-site/README.md +++ b/avm/res/web/static-site/README.md @@ -1090,7 +1090,7 @@ Array of role assignments to create. - `'Owner'` - `'Private DNS Zone Contributor'` - `'Reader'` - - `'Role Based Access Control Administrator (Preview)'` + - `'Role Based Access Control Administrator'` **Required parameters** @@ -1385,7 +1385,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | | `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep index f3387107ab..f4623d4a46 100644 --- a/avm/res/web/static-site/main.bicep +++ b/avm/res/web/static-site/main.bicep @@ -244,12 +244,12 @@ resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@202 } ] -module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ +module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-staticSite-PrivateEndpoint-${index}' scope: resourceGroup( - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[2], - split(privateEndpoint.?resourceGroupResourceId ?? privateEndpoint.?subnetResourceId, '/')[4] + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] ) params: { name: privateEndpoint.?name ?? 'pep-${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}' @@ -318,12 +318,39 @@ output location string = staticSite.location output defaultHostname string = staticSite.properties.defaultHostname @description('The private endpoints of the static site.') -output privateEndpoints array = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: staticSite_privateEndpoints[i].outputs.name - resourceId: staticSite_privateEndpoints[i].outputs.resourceId - groupId: staticSite_privateEndpoints[i].outputs.groupId - customDnsConfig: staticSite_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: staticSite_privateEndpoints[i].outputs.networkInterfaceIds +output privateEndpoints privateEndpointOutputType[] = [ + for (item, index) in (privateEndpoints ?? []): { + name: staticSite_privateEndpoints[index].outputs.name + resourceId: staticSite_privateEndpoints[index].outputs.resourceId + groupId: staticSite_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: staticSite_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: staticSite_privateEndpoints[index].outputs.networkInterfaceResourceIds } ] + +// ================ // +// Definitions // +// ================ // +@export() +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string + + @description('The resource ID of the private endpoint.') + resourceId: string + + @description('The group Id for the private endpoint Group.') + groupId: string? + + @description('The custom DNS configurations of the private endpoint.') + customDnsConfigs: { + @description('FQDN that resolves to private endpoint IP address.') + fqdn: string? + + @description('A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[] + + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json index 4ce780a6b8..9ccfcda554 100644 --- a/avm/res/web/static-site/main.json +++ b/avm/res/web/static-site/main.json @@ -6,12 +6,75 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "5409964564427816888" + "templateHash": "12616456112717414583" }, "name": "Static Web Apps", "description": "This module deploys a Static Web App." }, "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, "_1.privateEndpointCustomDnsConfigType": { "type": "object", "properties": { @@ -1065,8 +1128,8 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-staticSite-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'subnetResourceId')), '/')[4]]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1119,12 +1182,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint." }, "definitions": { "privateDnsZoneGroupType": { @@ -1146,80 +1208,118 @@ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "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." + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "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\"." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, "lockType": { "type": "object", @@ -1244,182 +1344,108 @@ } } }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } - }, - "nullable": true + } }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } - }, - "nullable": true + } }, - "privateDnsZoneGroupConfigType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "privateDnsZoneResourceId": { + "roleDefinitionIdOrName": { "type": "string", "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." } } }, "metadata": { + "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -1439,6 +1465,9 @@ }, "applicationSecurityGroupResourceIds": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { "description": "Optional. Application security groups in which the private endpoint IP configuration is included." @@ -1452,7 +1481,11 @@ } }, "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." } @@ -1473,12 +1506,17 @@ }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -1491,21 +1529,33 @@ } }, "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { "description": "Optional. Custom DNS configurations." } }, "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." } }, "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, "enableTelemetry": { @@ -1534,7 +1584,7 @@ "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -1542,7 +1592,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1648,12 +1698,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5805178546717255803" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { "privateDnsZoneGroupConfigType": { @@ -1731,10 +1780,7 @@ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] + } } }, "outputs": { @@ -1796,26 +1842,33 @@ }, "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { "description": "The custom DNS configurations of the private endpoint." }, "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "networkInterfaceIds": { + "networkInterfaceResourceIds": { "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, "groupId": { "type": "string", + "nullable": true, "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" } } } @@ -1871,17 +1924,20 @@ }, "privateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { "description": "The private endpoints of the static site." }, "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { "name": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", "resourceId": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "groupId": "[tryGet(tryGet(reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('staticSite_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } } diff --git a/avm/res/web/static-site/version.json b/avm/res/web/static-site/version.json index 09c3664cec..b6668657e7 100644 --- a/avm/res/web/static-site/version.json +++ b/avm/res/web/static-site/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 5a2527b9b8ba3303e52f38d7b88e7e77aa883876 Mon Sep 17 00:00:00 2001 From: Alexander Sehr <ASehr@hotmail.de> Date: Wed, 19 Feb 2025 08:50:51 +0100 Subject: [PATCH 43/53] fix: KVLT - Changed GUID used by role assignment due to conflict (#4477) ## Description - Changed the GUID used by one role assignment as it let to consistent conflicts in main (out of the blue, I might add) - Implemented other changes to trigger a module publishing (as a previous change got not merged because of the previous issue) ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.container-registry.registry](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2FcontainerRegistryGUID&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] 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 --- avm/res/container-registry/registry/README.md | 6 +++--- .../container-registry/registry/main.bicep | 14 ++++++------- avm/res/container-registry/registry/main.json | 20 +++++++++---------- .../registry/tests/e2e/max/main.test.bicep | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/avm/res/container-registry/registry/README.md b/avm/res/container-registry/registry/README.md index fa70d39ffe..4c84a4c69b 100644 --- a/avm/res/container-registry/registry/README.md +++ b/avm/res/container-registry/registry/README.md @@ -305,7 +305,7 @@ module registry 'br/public:avm/res/container-registry/registry:<version>' = { ] roleAssignments: [ { - name: '60395919-cfd3-47bf-8349-775ddebb255e' + name: '8f8b1c39-827f-43e6-a457-98bb15b5dbdf' principalId: '<principalId>' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' @@ -453,7 +453,7 @@ module registry 'br/public:avm/res/container-registry/registry:<version>' = { "roleAssignments": { "value": [ { - "name": "60395919-cfd3-47bf-8349-775ddebb255e", + "name": "8f8b1c39-827f-43e6-a457-98bb15b5dbdf", "principalId": "<principalId>", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" @@ -583,7 +583,7 @@ param replications = [ ] param roleAssignments = [ { - name: '60395919-cfd3-47bf-8349-775ddebb255e' + name: '8f8b1c39-827f-43e6-a457-98bb15b5dbdf' principalId: '<principalId>' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' diff --git a/avm/res/container-registry/registry/main.bicep b/avm/res/container-registry/registry/main.bicep index 682774e4da..ee4fd6b958 100644 --- a/avm/res/container-registry/registry/main.bicep +++ b/avm/res/container-registry/registry/main.bicep @@ -225,22 +225,22 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { - name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + name: last(split(customerManagedKey.?keyVaultResourceId!, '/')) scope: resourceGroup( - split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], - split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4] + 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' + name: customerManagedKey.?keyName! } } resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { - name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + name: last(split(customerManagedKey.?userAssignedIdentityResourceId!, '/')) scope: resourceGroup( - split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], - split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4] + split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[2], + split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[4] ) } diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json index 6506d73f14..f5de253d59 100644 --- a/avm/res/container-registry/registry/main.json +++ b/avm/res/container-registry/registry/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "9895920276913879197" + "templateHash": "2125225472095331873" }, "name": "Azure Container Registries (ACR)", "description": "This module deploys an Azure Container Registry (ACR)." @@ -1224,9 +1224,9 @@ "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'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", @@ -1253,18 +1253,18 @@ "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'), '/'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" }, "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'), '/'))]" + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "registry": { "type": "Microsoft.ContainerRegistry/registries", diff --git a/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep b/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep index 36964a7bb1..1860cae15e 100644 --- a/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep +++ b/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep @@ -135,7 +135,7 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { - name: '60395919-cfd3-47bf-8349-775ddebb255e' + name: '8f8b1c39-827f-43e6-a457-98bb15b5dbdf' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' From 2bdee752e71ce1d54ab424d2d6a98f7b681d1dca Mon Sep 17 00:00:00 2001 From: Nate Arnold <arnoldna@users.noreply.github.com> Date: Wed, 19 Feb 2025 01:14:14 -0700 Subject: [PATCH 44/53] fix: avm/res/network/erport broken user defined type (#4473) ## Description Fixes #4472 Closes #4472 - User Defined Type "links" misconfigured. - Added Tag parameter ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.network.express-route-port](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.network.express-route-port.yml/badge.svg?branch=avm%2Fres%2Fnetwork%2FerPort)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.network.express-route-port.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting 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`: - [X] 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. - [X] 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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/res/network/express-route-port/README.md | 69 +++++++++---------- avm/res/network/express-route-port/main.bicep | 18 +++-- avm/res/network/express-route-port/main.json | 29 +++----- .../tests/e2e/max/main.test.bicep | 9 ++- .../network/express-route-port/version.json | 2 +- 5 files changed, 65 insertions(+), 62 deletions(-) diff --git a/avm/res/network/express-route-port/README.md b/avm/res/network/express-route-port/README.md index f53ab6f595..bd7c23ebb1 100644 --- a/avm/res/network/express-route-port/README.md +++ b/avm/res/network/express-route-port/README.md @@ -117,7 +117,14 @@ module expressRoutePort 'br/public:avm/res/network/express-route-port:<version>' // Non-required parameters billingType: 'MeteredData' encapsulation: 'Dot1Q' - links: [] + links: [ + { + name: 'link1' + properties: { + adminState: 'Disabled' + } + } + ] location: '<location>' lock: { kind: 'CanNotDelete' @@ -176,7 +183,14 @@ module expressRoutePort 'br/public:avm/res/network/express-route-port:<version>' "value": "Dot1Q" }, "links": { - "value": [] + "value": [ + { + "name": "link1", + "properties": { + "adminState": "Disabled" + } + } + ] }, "location": { "value": "<location>" @@ -229,7 +243,14 @@ param peeringLocation = 'Airtel-Chennai2-CLS' // Non-required parameters param billingType = 'MeteredData' param encapsulation = 'Dot1Q' -param links = [] +param links = [ + { + name: 'link1' + properties: { + adminState: 'Disabled' + } + } +] param location = '<location>' param lock = { kind: 'CanNotDelete' @@ -346,6 +367,7 @@ param peeringLocation = 'Airtel-Chennai2-CLS' | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | ### Parameter: `bandwidthInGbps` @@ -432,12 +454,6 @@ The name of the link to be created. - Required: Yes - Type: string -- Allowed: - ```Bicep - [ - 'string' - ] - ``` ### Parameter: `links.id` @@ -445,12 +461,6 @@ Resource Id of the existing Link. - Required: No - Type: string -- Allowed: - ```Bicep - [ - 'string' - ] - ``` ### Parameter: `links.properties` @@ -463,7 +473,7 @@ Properties of the Link. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`adminState`](#parameter-linkspropertiesadminstate) | string | Administrative state of the physical port. | +| [`adminState`](#parameter-linkspropertiesadminstate) | string | Administrative state of the physical port. Must be set to 'Disabled' for initial deployment. | **Optional parameters** @@ -473,16 +483,10 @@ Properties of the Link. ### Parameter: `links.properties.adminState` -Administrative state of the physical port. +Administrative state of the physical port. Must be set to 'Disabled' for initial deployment. - Required: Yes - Type: string -- Allowed: - ```Bicep - [ - 'string' - ] - ``` ### Parameter: `links.properties.macSecConfig` @@ -506,12 +510,6 @@ Keyvault Secret Identifier URL containing Mac security CAK key. - Required: Yes - Type: string -- Allowed: - ```Bicep - [ - 'string' - ] - ``` ### Parameter: `links.properties.macSecConfig.cipher` @@ -535,12 +533,6 @@ Keyvault Secret Identifier URL containing Mac security CKN key. - Required: Yes - Type: string -- Allowed: - ```Bicep - [ - 'string' - ] - ``` ### Parameter: `links.properties.macSecConfig.sciState` @@ -704,6 +696,13 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + ## Outputs | Output | Type | Description | diff --git a/avm/res/network/express-route-port/main.bicep b/avm/res/network/express-route-port/main.bicep index 8882ad138e..c06b135b6e 100644 --- a/avm/res/network/express-route-port/main.bicep +++ b/avm/res/network/express-route-port/main.bicep @@ -41,6 +41,9 @@ param roleAssignments roleAssignmentType[]? @description('Optional. Location for all resources.') param location string = resourceGroup().location +@description('Optional. Tags of the resource.') +param tags object? + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') 'Network Contributor': subscriptionResourceId( @@ -103,6 +106,7 @@ resource expressRoutePort_lock 'Microsoft.Authorization/locks@2020-05-01' = if ( resource expressRoutePort 'Microsoft.Network/ExpressRoutePorts@2024-05-01' = { name: name location: location + tags: tags properties: { bandwidthInGbps: bandwidthInGbps billingType: billingType @@ -121,7 +125,7 @@ resource expressRoutePort_roleAssignments 'Microsoft.Authorization/roleAssignmen description: roleAssignment.?description principalType: roleAssignment.?principalType condition: roleAssignment.?condition - conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condition is set delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId } scope: expressRoutePort @@ -148,26 +152,26 @@ output location string = expressRoutePort.location @description('The type for a link.') type linkType = { @description('Optional. Resource Id of the existing Link.') - id: 'string'? + id: string? @description('Required. The name of the link to be created.') - name: 'string' + name: string @description('Optional. Properties of the Link.') properties: { - @description('Required. Administrative state of the physical port.') - adminState: 'string' + @description('Required. Administrative state of the physical port. Must be set to \'Disabled\' for initial deployment.') + adminState: string @description('Optional. MacSec Configuration of the link.') macSecConfig: { @description('Required. Keyvault Secret Identifier URL containing Mac security CAK key.') - cakSecretIdentifier: 'string' + cakSecretIdentifier: string @description('Required. Mac security cipher.') cipher: ('GcmAes128' | 'GcmAes256' | 'GcmAesXpn128' | 'GcmAesXpn256') @description('Required. Keyvault Secret Identifier URL containing Mac security CKN key.') - cknSecretIdentifier: 'string' + cknSecretIdentifier: string @description('Required. Sci mode.') sciState: ('Enabled' | 'Disabled') diff --git a/avm/res/network/express-route-port/main.json b/avm/res/network/express-route-port/main.json index ac0e613091..859b8f17c8 100644 --- a/avm/res/network/express-route-port/main.json +++ b/avm/res/network/express-route-port/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "13147305000786724307" + "version": "0.33.13.18514", + "templateHash": "12646697926659678830" }, "name": "ExpressRoute Ports", "description": "This module deploys an Express Route Port resource used by Express Route Direct." @@ -17,9 +17,6 @@ "properties": { "id": { "type": "string", - "allowedValues": [ - "string" - ], "nullable": true, "metadata": { "description": "Optional. Resource Id of the existing Link." @@ -27,9 +24,6 @@ }, "name": { "type": "string", - "allowedValues": [ - "string" - ], "metadata": { "description": "Required. The name of the link to be created." } @@ -39,11 +33,8 @@ "properties": { "adminState": { "type": "string", - "allowedValues": [ - "string" - ], "metadata": { - "description": "Required. Administrative state of the physical port." + "description": "Required. Administrative state of the physical port. Must be set to 'Disabled' for initial deployment." } }, "macSecConfig": { @@ -51,9 +42,6 @@ "properties": { "cakSecretIdentifier": { "type": "string", - "allowedValues": [ - "string" - ], "metadata": { "description": "Required. Keyvault Secret Identifier URL containing Mac security CAK key." } @@ -72,9 +60,6 @@ }, "cknSecretIdentifier": { "type": "string", - "allowedValues": [ - "string" - ], "metadata": { "description": "Required. Keyvault Secret Identifier URL containing Mac security CKN key." } @@ -294,6 +279,13 @@ "metadata": { "description": "Optional. Location for all resources." } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } } }, "variables": { @@ -353,6 +345,7 @@ "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { "bandwidthInGbps": "[parameters('bandwidthInGbps')]", "billingType": "[parameters('billingType')]", diff --git a/avm/res/network/express-route-port/tests/e2e/max/main.test.bicep b/avm/res/network/express-route-port/tests/e2e/max/main.test.bicep index cce053cfe6..81db35fdd4 100644 --- a/avm/res/network/express-route-port/tests/e2e/max/main.test.bicep +++ b/avm/res/network/express-route-port/tests/e2e/max/main.test.bicep @@ -56,7 +56,14 @@ module testDeployment '../../../main.bicep' = [ bandwidthInGbps: 10 billingType: 'MeteredData' encapsulation: 'Dot1Q' - links: [] + links: [ + { + name: 'link1' + properties: { + adminState: 'Disabled' + } + } + ] lock: { kind: 'CanNotDelete' name: 'myCustomLockName' diff --git a/avm/res/network/express-route-port/version.json b/avm/res/network/express-route-port/version.json index 8def869ede..daf1a794d9 100644 --- a/avm/res/network/express-route-port/version.json +++ b/avm/res/network/express-route-port/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] From 614ddd0f27476f5a242fe6745442ef7d2dd28767 Mon Sep 17 00:00:00 2001 From: JeffreyCA <jeffreychen@microsoft.com> Date: Wed, 19 Feb 2025 09:50:51 -0800 Subject: [PATCH 45/53] feat: New module `avm/res/cache/redis-enterprise` - Redis Enterprise and Azure Managed Redis (Preview) (#4114) ## Description Initial implementation of Bicep AVM for Redis Enterprise and Azure Managed Redis (Preview) caches (`Microsoft.Cache/redisEnterprise`). There is a related module, [`avm/res/cache/redis`](https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/cache/redis), that covers `Microsoft.Cache/redis`: | Module Name | Resource Type | SKUs | |----------------------------|--------------------------------|--------------------------------------------------------------------------------------------| | [`avm/res/cache/redis`](https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/cache/redis) | Microsoft.Cache/redis | **OSS Redis:** Basic, Standard, Premium | | `avm/res/cache/redis-enterprise` (this PR) | Microsoft.Cache/redisEnterprise | **Redis Enterprise:** Enterprise, Enterprise Flash<br>**Azure Managed Redis (Preview):** Balanced, Compute Optimized, Flash Optimized, Memory Optimized | ![image](https://github.com/user-attachments/assets/7128fcea-0708-4ae5-b2ed-8e1e1b9dd6ff) Closes https://github.com/Azure/Azure-Verified-Modules/issues/1731. ## Pipeline Reference I'm facing issues setting up CI on my fork using my Azure subscription and Service Principal in the Microsoft tenant, but was able to validate all the E2E tests passed using [`Test-ModuleLocally.ps1`](https://github.com/Azure/bicep-registry-modules/blob/main/utilities/tools/Test-ModuleLocally.ps1). ![image](https://github.com/user-attachments/assets/9a8de7d8-598e-41b0-8ebc-3ae68ceb697b) <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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`. - [x] 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. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings <!-- Please keep up to date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.res.cache.redis-enterprise.yml | 88 + avm/res/cache/redis-enterprise/README.md | 2659 +++++++++++++ .../cache/redis-enterprise/database/README.md | 521 +++ .../access-policy-assignment/README.md | 88 + .../access-policy-assignment/main.bicep | 51 + .../access-policy-assignment/main.json | 106 + .../redis-enterprise/database/main.bicep | 329 ++ .../cache/redis-enterprise/database/main.json | 933 +++++ .../database/modules/keyVaultExport.bicep | 43 + avm/res/cache/redis-enterprise/main.bicep | 517 +++ avm/res/cache/redis-enterprise/main.json | 3312 +++++++++++++++++ .../active-geo-replication/dependencies.bicep | 42 + .../active-geo-replication/main.test.bicep | 74 + .../tests/e2e/amr-entra-id/dependencies.bicep | 13 + .../tests/e2e/amr-entra-id/main.test.bicep | 68 + .../tests/e2e/amr/main.test.bicep | 49 + .../tests/e2e/defaults/main.test.bicep | 48 + .../tests/e2e/kv-secrets/dependencies.bicep | 21 + .../tests/e2e/kv-secrets/main.test.bicep | 62 + .../tests/e2e/max/dependencies.bicep | 63 + .../tests/e2e/max/main.test.bicep | 175 + .../dependencies.bicep | 63 + .../main.test.bicep | 75 + .../tests/e2e/waf-aligned/dependencies.bicep | 49 + .../tests/e2e/waf-aligned/main.test.bicep | 132 + avm/res/cache/redis-enterprise/version.json | 7 + 28 files changed, 9590 insertions(+) create mode 100644 .github/workflows/avm.res.cache.redis-enterprise.yml create mode 100644 avm/res/cache/redis-enterprise/README.md create mode 100644 avm/res/cache/redis-enterprise/database/README.md create mode 100644 avm/res/cache/redis-enterprise/database/access-policy-assignment/README.md create mode 100644 avm/res/cache/redis-enterprise/database/access-policy-assignment/main.bicep create mode 100644 avm/res/cache/redis-enterprise/database/access-policy-assignment/main.json create mode 100644 avm/res/cache/redis-enterprise/database/main.bicep create mode 100644 avm/res/cache/redis-enterprise/database/main.json create mode 100644 avm/res/cache/redis-enterprise/database/modules/keyVaultExport.bicep create mode 100644 avm/res/cache/redis-enterprise/main.bicep create mode 100644 avm/res/cache/redis-enterprise/main.json create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/amr/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/max/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/cache/redis-enterprise/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bad9d11c98..706ed99129 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -50,6 +50,7 @@ /avm/res/azure-stack-hci/cluster/ @Azure/avm-res-azurestackhci-cluster-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/batch/batch-account/ @Azure/avm-res-batch-batchaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/cache/redis/ @Azure/avm-res-cache-redis-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/cache/redis-enterprise/ @Azure/avm-res-cache-redisenterprise-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/cdn/profile/ @Azure/avm-res-cdn-profile-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/cognitive-services/account/ @Azure/avm-res-cognitiveservices-account-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/communication/communication-service/ @Azure/avm-res-communication-communicationservice-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index b0315b0a37..f6162bc1c6 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -85,6 +85,7 @@ body: - "avm/res/azure-stack-hci/cluster" - "avm/res/batch/batch-account" - "avm/res/cache/redis" + - "avm/res/cache/redis-enterprise" - "avm/res/cdn/profile" - "avm/res/cognitive-services/account" - "avm/res/communication/communication-service" diff --git a/.github/workflows/avm.res.cache.redis-enterprise.yml b/.github/workflows/avm.res.cache.redis-enterprise.yml new file mode 100644 index 0000000000..7cdf12fdd4 --- /dev/null +++ b/.github/workflows/avm.res.cache.redis-enterprise.yml @@ -0,0 +1,88 @@ +name: "avm.res.cache.redis-enterprise" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.cache.redis-enterprise.yml" + - "avm/res/cache/redis-enterprise/**" + - "utilities/pipelines/**" + - "!utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/cache/redis-enterprise" + workflowPath: ".github/workflows/avm.res.cache.redis-enterprise.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/cache/redis-enterprise/README.md b/avm/res/cache/redis-enterprise/README.md new file mode 100644 index 0000000000..5dc42f9da4 --- /dev/null +++ b/avm/res/cache/redis-enterprise/README.md @@ -0,0 +1,2659 @@ +# Redis Enterprise and Azure Managed Redis (Preview) `[Microsoft.Cache/redisEnterprise]` + +This module deploys a Redis Enterprise or Azure Managed Redis (Preview) cache. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## 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.Cache/redisEnterprise` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise) | +| `Microsoft.Cache/redisEnterprise/databases` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise/databases) | +| `Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise/databases/accessPolicyAssignments) | +| `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) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/cache/redis-enterprise:<version>`. + +- [Active geo-replication](#example-1-active-geo-replication) +- [Azure Managed Redis (Preview) with Entra ID authentication](#example-2-azure-managed-redis-preview-with-entra-id-authentication) +- [Azure Managed Redis (Preview)](#example-3-azure-managed-redis-preview) +- [Using only defaults](#example-4-using-only-defaults) +- [Deploying with a key vault reference to save secrets](#example-5-deploying-with-a-key-vault-reference-to-save-secrets) +- [Using large parameter set](#example-6-using-large-parameter-set) +- [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: _Active geo-replication_ + +This instance deploys the module with active geo-replication enabled. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'creagr002' + // Non-required parameters + database: { + geoReplication: { + groupNickname: '<groupNickname>' + linkedDatabases: [ + { + id: '<id>' + } + { + id: '<id>' + } + ] + } + } + location: '<location>' + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "creagr002" + }, + // Non-required parameters + "database": { + "value": { + "geoReplication": { + "groupNickname": "<groupNickname>", + "linkedDatabases": [ + { + "id": "<id>" + }, + { + "id": "<id>" + } + ] + } + } + }, + "location": { + "value": "<location>" + }, + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'creagr002' +// Non-required parameters +param database = { + geoReplication: { + groupNickname: '<groupNickname>' + linkedDatabases: [ + { + id: '<id>' + } + { + id: '<id>' + } + ] + } +} +param location = '<location>' +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 2: _Azure Managed Redis (Preview) with Entra ID authentication_ + +This instance deploys an Azure Managed Redis (Preview) cache with Entra ID authentication. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'creaei001' + // Non-required parameters + database: { + accessKeysAuthentication: 'Disabled' + accessPolicyAssignments: [ + { + name: 'assign1' + userObjectId: '<userObjectId>' + } + ] + } + location: '<location>' + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "creaei001" + }, + // Non-required parameters + "database": { + "value": { + "accessKeysAuthentication": "Disabled", + "accessPolicyAssignments": [ + { + "name": "assign1", + "userObjectId": "<userObjectId>" + } + ] + } + }, + "location": { + "value": "<location>" + }, + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'creaei001' +// Non-required parameters +param database = { + accessKeysAuthentication: 'Disabled' + accessPolicyAssignments: [ + { + name: 'assign1' + userObjectId: '<userObjectId>' + } + ] +} +param location = '<location>' +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 3: _Azure Managed Redis (Preview)_ + +This instance deploys an Azure Managed Redis (Preview) cache with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'creamr001' + // Non-required parameters + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "creamr001" + }, + // Non-required parameters + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'creamr001' +// Non-required parameters +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 4: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'cremin001' + // Non-required parameters + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "cremin001" + }, + // Non-required parameters + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'cremin001' +// Non-required parameters +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 5: _Deploying with a key vault reference to save secrets_ + +This instance deploys the module saving all its secrets in a key vault. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'kvref' + // Non-required parameters + database: { + secretsExportConfiguration: { + keyVaultResourceId: '<keyVaultResourceId>' + primaryAccessKeyName: 'custom-primaryAccessKey-name' + primaryConnectionStringName: 'custom-primaryConnectionString-name' + primaryStackExchangeRedisConnectionStringName: 'custom-primaryStackExchangeRedisConnectionString-name' + secondaryAccessKeyName: 'custom-secondaryAccessKey-name' + secondaryConnectionStringName: 'custom-secondaryConnectionString-name' + secondaryStackExchangeRedisConnectionStringName: 'custom-secondaryStackExchangeRedisConnectionString-name' + } + } + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kvref" + }, + // Non-required parameters + "database": { + "value": { + "secretsExportConfiguration": { + "keyVaultResourceId": "<keyVaultResourceId>", + "primaryAccessKeyName": "custom-primaryAccessKey-name", + "primaryConnectionStringName": "custom-primaryConnectionString-name", + "primaryStackExchangeRedisConnectionStringName": "custom-primaryStackExchangeRedisConnectionString-name", + "secondaryAccessKeyName": "custom-secondaryAccessKey-name", + "secondaryConnectionStringName": "custom-secondaryConnectionString-name", + "secondaryStackExchangeRedisConnectionStringName": "custom-secondaryStackExchangeRedisConnectionString-name" + } + } + }, + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'kvref' +// Non-required parameters +param database = { + secretsExportConfiguration: { + keyVaultResourceId: '<keyVaultResourceId>' + primaryAccessKeyName: 'custom-primaryAccessKey-name' + primaryConnectionStringName: 'custom-primaryConnectionString-name' + primaryStackExchangeRedisConnectionStringName: 'custom-primaryStackExchangeRedisConnectionString-name' + secondaryAccessKeyName: 'custom-secondaryAccessKey-name' + secondaryConnectionStringName: 'custom-secondaryConnectionString-name' + secondaryStackExchangeRedisConnectionStringName: 'custom-secondaryStackExchangeRedisConnectionString-name' + } +} +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 6: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'cremax001' + // Non-required parameters + database: { + clientProtocol: 'Plaintext' + clusteringPolicy: 'EnterpriseCluster' + deferUpgrade: 'Deferred' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSettingDatabase' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + evictionPolicy: 'NoEviction' + modules: [ + { + name: 'RedisBloom' + } + { + name: 'RediSearch' + } + ] + persistence: { + frequency: '1s' + type: 'aof' + } + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSettingCluster' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + enableTelemetry: true + highAvailability: 'Disabled' + location: '<location>' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + minimumTlsVersion: '1.2' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '<privateDnsZoneResourceId>' + } + ] + } + subnetResourceId: '<subnetResourceId>' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + name: '759769d2-fc52-4a92-a943-724e48927e0b' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '<name>' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '<roleDefinitionIdOrName>' + } + ] + skuName: 'Balanced_B10' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + zones: [ + 1 + 2 + 3 + ] + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "cremax001" + }, + // Non-required parameters + "database": { + "value": { + "clientProtocol": "Plaintext", + "clusteringPolicy": "EnterpriseCluster", + "deferUpgrade": "Deferred", + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSettingDatabase", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ], + "evictionPolicy": "NoEviction", + "modules": [ + { + "name": "RedisBloom" + }, + { + "name": "RediSearch" + } + ], + "persistence": { + "frequency": "1s", + "type": "aof" + } + } + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSettingCluster", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ] + }, + "enableTelemetry": { + "value": true + }, + "highAvailability": { + "value": "Disabled" + }, + "location": { + "value": "<location>" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "minimumTlsVersion": { + "value": "1.2" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "<privateDnsZoneResourceId>" + } + ] + }, + "subnetResourceId": "<subnetResourceId>", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "name": "759769d2-fc52-4a92-a943-724e48927e0b", + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "<name>", + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "<roleDefinitionIdOrName>" + } + ] + }, + "skuName": { + "value": "Balanced_B10" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "zones": { + "value": [ + 1, + 2, + 3 + ] + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'cremax001' +// Non-required parameters +param database = { + clientProtocol: 'Plaintext' + clusteringPolicy: 'EnterpriseCluster' + deferUpgrade: 'Deferred' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSettingDatabase' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + evictionPolicy: 'NoEviction' + modules: [ + { + name: 'RedisBloom' + } + { + name: 'RediSearch' + } + ] + persistence: { + frequency: '1s' + type: 'aof' + } +} +param diagnosticSettings = [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSettingCluster' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } +] +param enableTelemetry = true +param highAvailability = 'Disabled' +param location = '<location>' +param lock = { + kind: 'CanNotDelete' + name: 'myCustomLockName' +} +param minimumTlsVersion = '1.2' +param privateEndpoints = [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '<privateDnsZoneResourceId>' + } + ] + } + subnetResourceId: '<subnetResourceId>' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +] +param roleAssignments = [ + { + name: '759769d2-fc52-4a92-a943-724e48927e0b' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '<name>' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '<roleDefinitionIdOrName>' + } +] +param skuName = 'Balanced_B10' +param tags = { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' +} +param zones = [ + 1 + 2 + 3 +] +``` + +</details> +<p> + +### 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. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'creuace001' + // Non-required parameters + customerManagedKey: { + keyName: '<keyName>' + keyVaultResourceId: '<keyVaultResourceId>' + userAssignedIdentityResourceId: '<userAssignedIdentityResourceId>' + } + database: { + persistence: { + frequency: '6h' + type: 'rdb' + } + } + managedIdentities: { + userAssignedResourceIds: [ + '<managedIdentityResourceId>' + ] + } + skuName: 'Balanced_B10' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "creuace001" + }, + // Non-required parameters + "customerManagedKey": { + "value": { + "keyName": "<keyName>", + "keyVaultResourceId": "<keyVaultResourceId>", + "userAssignedIdentityResourceId": "<userAssignedIdentityResourceId>" + } + }, + "database": { + "value": { + "persistence": { + "frequency": "6h", + "type": "rdb" + } + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "<managedIdentityResourceId>" + ] + } + }, + "skuName": { + "value": "Balanced_B10" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'creuace001' +// Non-required parameters +param customerManagedKey = { + keyName: '<keyName>' + keyVaultResourceId: '<keyVaultResourceId>' + userAssignedIdentityResourceId: '<userAssignedIdentityResourceId>' +} +param database = { + persistence: { + frequency: '6h' + type: 'rdb' + } +} +param managedIdentities = { + userAssignedResourceIds: [ + '<managedIdentityResourceId>' + ] +} +param skuName = 'Balanced_B10' +``` + +</details> +<p> + +### Example 8: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module redisEnterprise 'br/public:avm/res/cache/redis-enterprise:<version>' = { + name: 'redisEnterpriseDeployment' + params: { + // Required parameters + name: 'crewaf001' + // Non-required parameters + database: { + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSettingDatabase' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + persistence: { + frequency: '1h' + type: 'rdb' + } + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSettingCluster' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '<privateDnsZoneResourceId>' + } + ] + } + subnetResourceId: '<subnetResourceId>' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + ] + skuName: 'Balanced_B10' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + zones: [ + 1 + 2 + 3 + ] + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "crewaf001" + }, + // Non-required parameters + "database": { + "value": { + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSettingDatabase", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ], + "persistence": { + "frequency": "1h", + "type": "rdb" + } + } + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSettingCluster", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "<privateDnsZoneResourceId>" + } + ] + }, + "subnetResourceId": "<subnetResourceId>", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + ] + }, + "skuName": { + "value": "Balanced_B10" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "zones": { + "value": [ + 1, + 2, + 3 + ] + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/cache/redis-enterprise:<version>' + +// Required parameters +param name = 'crewaf001' +// Non-required parameters +param database = { + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSettingDatabase' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + persistence: { + frequency: '1h' + type: 'rdb' + } +} +param diagnosticSettings = [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSettingCluster' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } +] +param privateEndpoints = [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '<privateDnsZoneResourceId>' + } + ] + } + subnetResourceId: '<subnetResourceId>' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +] +param skuName = 'Balanced_B10' +param tags = { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' +} +param zones = [ + 1 + 2 + 3 +] +``` + +</details> +<p> + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the cache resource. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. Required if 'customerManagedKey' is not empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`capacity`](#parameter-capacity) | int | The size of the cluster. Only supported on Redis Enterprise SKUs: Enterprise, EnterpriseFlash. Valid values are (2, 4, 6, 8, 10) for Enterprise SKUs and (3, 9) for EnterpriseFlash SKUs. [Learn more](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-enterprise-tiers#sharding-and-cpu-utilization). | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition to use for the managed service. | +| [`database`](#parameter-database) | object | Database configuration. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The cluster-level diagnostic settings of the service. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`highAvailability`](#parameter-highavailability) | string | Specifies whether to enable data replication for high availability. Used only with Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. HIGH AVAILABILITY IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-high-availability) FOR CLARIFICATION. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`minimumTlsVersion`](#parameter-minimumtlsversion) | string | The minimum TLS version for the Redis cluster to support. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`skuName`](#parameter-skuname) | string | The type of cluster to deploy. Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized ARE IN PREVIEW, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview#tiers-and-skus-at-a-glance) FOR CLARIFICATION. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`zones`](#parameter-zones) | array | The Availability Zones to place the resources in. Currently only supported on Enterprise and EnterpriseFlash SKUs. | + +### Parameter: `name` + +The name of the cache resource. + +- Required: Yes +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. Required if 'customerManagedKey' is not empty. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. + +- Required: No +- Type: array + +### Parameter: `capacity` + +The size of the cluster. Only supported on Redis Enterprise SKUs: Enterprise, EnterpriseFlash. Valid values are (2, 4, 6, 8, 10) for Enterprise SKUs and (3, 9) for EnterpriseFlash SKUs. [Learn more](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-enterprise-tiers#sharding-and-cpu-utilization). + +- Required: No +- Type: int +- Default: `2` +- Allowed: + ```Bicep + [ + 2 + 3 + 4 + 6 + 8 + 9 + 10 + ] + ``` + +### Parameter: `customerManagedKey` + +The customer managed key definition to use for the managed service. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | string | The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +The name of the customer managed key to use for encryption. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVaultResourceId` + +The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVersion` + +The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time. + +- Required: No +- Type: string + +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. + +- Required: No +- Type: string + +### Parameter: `database` + +Database configuration. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessKeysAuthentication`](#parameter-databaseaccesskeysauthentication) | string | Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION. | +| [`accessPolicyAssignments`](#parameter-databaseaccesspolicyassignments) | array | Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION. | +| [`clientProtocol`](#parameter-databaseclientprotocol) | string | Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols. | +| [`clusteringPolicy`](#parameter-databaseclusteringpolicy) | string | Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering). | +| [`deferUpgrade`](#parameter-databasedeferupgrade) | string | Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades). | +| [`diagnosticSettings`](#parameter-databasediagnosticsettings) | array | The database-level diagnostic settings of the service. | +| [`evictionPolicy`](#parameter-databaseevictionpolicy) | string | Specifies the eviction policy for the Redis resource. | +| [`geoReplication`](#parameter-databasegeoreplication) | object | The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration. | +| [`modules`](#parameter-databasemodules) | array | Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules). | +| [`name`](#parameter-databasename) | string | Name of the database. | +| [`persistence`](#parameter-databasepersistence) | object | The persistence settings of the service. | +| [`port`](#parameter-databaseport) | int | TCP port of the database endpoint. | +| [`secretsExportConfiguration`](#parameter-databasesecretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | + +### Parameter: `database.accessKeysAuthentication` + +Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `database.accessPolicyAssignments` + +Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userObjectId`](#parameter-databaseaccesspolicyassignmentsuserobjectid) | string | Object ID to which the access policy will be assigned. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessPolicyName`](#parameter-databaseaccesspolicyassignmentsaccesspolicyname) | string | Name of the access policy to be assigned. The current only allowed name is 'default'. | +| [`name`](#parameter-databaseaccesspolicyassignmentsname) | string | Name of the access policy assignment. | + +### Parameter: `database.accessPolicyAssignments.userObjectId` + +Object ID to which the access policy will be assigned. + +- Required: Yes +- Type: string + +### Parameter: `database.accessPolicyAssignments.accessPolicyName` + +Name of the access policy to be assigned. The current only allowed name is 'default'. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `database.accessPolicyAssignments.name` + +Name of the access policy assignment. + +- Required: No +- Type: string + +### Parameter: `database.clientProtocol` + +Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Encrypted' + 'Plaintext' + ] + ``` + +### Parameter: `database.clusteringPolicy` + +Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'EnterpriseCluster' + 'OSSCluster' + ] + ``` + +### Parameter: `database.deferUpgrade` + +Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Deferred' + 'NotDeferred' + ] + ``` + +### Parameter: `database.diagnosticSettings` + +The database-level diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-databasediagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-databasediagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-databasediagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-databasediagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-databasediagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`name`](#parameter-databasediagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-databasediagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`workspaceResourceId`](#parameter-databasediagnosticsettingsworkspaceresourceid) | string | 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. | + +### Parameter: `database.diagnosticSettings.eventHubAuthorizationRuleResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `database.diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-databasediagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-databasediagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-databasediagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `database.diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `database.diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `database.diagnosticSettings.workspaceResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `database.evictionPolicy` + +Specifies the eviction policy for the Redis resource. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AllKeysLFU' + 'AllKeysLRU' + 'AllKeysRandom' + 'NoEviction' + 'VolatileLFU' + 'VolatileLRU' + 'VolatileRandom' + 'VolatileTTL' + ] + ``` + +### Parameter: `database.geoReplication` + +The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupNickname`](#parameter-databasegeoreplicationgroupnickname) | string | The name of the geo-replication group. | +| [`linkedDatabases`](#parameter-databasegeoreplicationlinkeddatabases) | array | List of database resources to link with this database, including itself. | + +### Parameter: `database.geoReplication.groupNickname` + +The name of the geo-replication group. + +- Required: Yes +- Type: string + +### Parameter: `database.geoReplication.linkedDatabases` + +List of database resources to link with this database, including itself. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-databasegeoreplicationlinkeddatabasesid) | string | Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`. | + +### Parameter: `database.geoReplication.linkedDatabases.id` + +Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`. + +- Required: Yes +- Type: string + +### Parameter: `database.modules` + +Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules). + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-databasemodulesname) | string | The name of the module. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`args`](#parameter-databasemodulesargs) | string | Additional module arguments. | + +### Parameter: `database.modules.name` + +The name of the module. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'RedisBloom' + 'RediSearch' + 'RedisJSON' + 'RedisTimeSeries' + ] + ``` + +### Parameter: `database.modules.args` + +Additional module arguments. + +- Required: No +- Type: string + +### Parameter: `database.name` + +Name of the database. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `database.persistence` + +The persistence settings of the service. + +- Required: No +- Type: object + +### Parameter: `database.port` + +TCP port of the database endpoint. + +- Required: No +- Type: int +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object +- MinValue: 10000 +- MaxValue: 10000 + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-databasesecretsexportconfigurationkeyvaultresourceid) | string | The resource ID of the key vault where to store the secrets of this module. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`primaryAccessKeyName`](#parameter-databasesecretsexportconfigurationprimaryaccesskeyname) | string | The primaryAccessKey secret name to create. | +| [`primaryConnectionStringName`](#parameter-databasesecretsexportconfigurationprimaryconnectionstringname) | string | The primaryConnectionString secret name to create. | +| [`primaryStackExchangeRedisConnectionStringName`](#parameter-databasesecretsexportconfigurationprimarystackexchangeredisconnectionstringname) | string | The primaryStackExchangeRedisConnectionString secret name to create. | +| [`secondaryAccessKeyName`](#parameter-databasesecretsexportconfigurationsecondaryaccesskeyname) | string | The secondaryAccessKey secret name to create. | +| [`secondaryConnectionStringName`](#parameter-databasesecretsexportconfigurationsecondaryconnectionstringname) | string | The secondaryConnectionString secret name to create. | +| [`secondaryStackExchangeRedisConnectionStringName`](#parameter-databasesecretsexportconfigurationsecondarystackexchangeredisconnectionstringname) | string | The secondaryStackExchangeRedisConnectionString secret name to create. | + +### Parameter: `database.secretsExportConfiguration.keyVaultResourceId` + +The resource ID of the key vault where to store the secrets of this module. + +- Required: Yes +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.primaryAccessKeyName` + +The primaryAccessKey secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.primaryConnectionStringName` + +The primaryConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.primaryStackExchangeRedisConnectionStringName` + +The primaryStackExchangeRedisConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.secondaryAccessKeyName` + +The secondaryAccessKey secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.secondaryConnectionStringName` + +The secondaryConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `database.secretsExportConfiguration.secondaryStackExchangeRedisConnectionStringName` + +The secondaryStackExchangeRedisConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `diagnosticSettings` + +The cluster-level diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `highAvailability` + +Specifies whether to enable data replication for high availability. Used only with Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. HIGH AVAILABILITY IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-high-availability) FOR CLARIFICATION. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `minimumTlsVersion` + +The minimum TLS version for the Redis cluster to support. + +- Required: No +- Type: string +- Default: `'1.2'` +- Allowed: + ```Bicep + [ + '1.2' + ] + ``` + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the Private Endpoint IP configuration is included. | +| [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | +| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the Private Endpoint. | +| [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | +| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the Private Endpoint to. | +| [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | +| [`name`](#parameter-privateendpointsname) | string | The name of the Private Endpoint. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS Zone Group to configure for the Private Endpoint. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | +| [`resourceGroupResourceId`](#parameter-privateendpointsresourcegroupresourceid) | string | The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. | +| [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. | +| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/Resource Groups in this deployment. | + +### Parameter: `privateEndpoints.subnetResourceId` + +Resource ID of the subnet where the endpoint needs to be created. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` + +Application security groups in which the Private Endpoint IP configuration is included. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.customDnsConfigs` + +Custom DNS configurations. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | FQDN that resolves to private endpoint IP address. | + +### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` + +A list of private IP addresses of the private endpoint. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints.customDnsConfigs.fqdn` + +FQDN that resolves to private endpoint IP address. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.customNetworkInterfaceName` + +The custom name of the network interface attached to the Private Endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.ipConfigurations` + +A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsipconfigurationsname) | string | The name of the resource that is unique within a resource group. | +| [`properties`](#parameter-privateendpointsipconfigurationsproperties) | object | Properties of private endpoint IP configurations. | + +### Parameter: `privateEndpoints.ipConfigurations.name` + +The name of the resource that is unique within a resource group. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties` + +Properties of private endpoint IP configurations. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | +| [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | + +### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` + +The ID of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.memberName` + +The member name of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` + +A private IP address obtained from the private endpoint's subnet. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.location` + +The location to deploy the Private Endpoint to. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.lock` + +Specify the type of lock. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-privateendpointslockkind) | string | Specify the type of lock. | +| [`name`](#parameter-privateendpointslockname) | string | Specify the name of lock. | + +### Parameter: `privateEndpoints.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `privateEndpoints.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.manualConnectionRequestMessage` + +A message passed to the owner of the remote resource with the manual connection request. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.name` + +The name of the Private Endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS Zone Group to configure for the Private Endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS Zone Group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS Zone Group config. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupResourceId` + +The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-privateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-privateendpointsroleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-privateendpointsroleassignmentscondition) | string | 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`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `privateEndpoints.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `privateEndpoints.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `privateEndpoints.service` + +The subresource to deploy the Private Endpoint for. For example "vault" for a Key Vault Private Endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.tags` + +Tags to be applied on all resources/Resource Groups in this deployment. + +- Required: No +- Type: object + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Redis Cache Contributor'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `skuName` + +The type of cluster to deploy. Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized ARE IN PREVIEW, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview#tiers-and-skus-at-a-glance) FOR CLARIFICATION. + +- Required: No +- Type: string +- Default: `'Enterprise_E5'` +- Allowed: + ```Bicep + [ + 'Balanced_B0' + 'Balanced_B1' + 'Balanced_B10' + 'Balanced_B100' + 'Balanced_B1000' + 'Balanced_B150' + 'Balanced_B20' + 'Balanced_B250' + 'Balanced_B3' + 'Balanced_B350' + 'Balanced_B5' + 'Balanced_B50' + 'Balanced_B500' + 'Balanced_B700' + 'ComputeOptimized_X10' + 'ComputeOptimized_X100' + 'ComputeOptimized_X150' + 'ComputeOptimized_X20' + 'ComputeOptimized_X250' + 'ComputeOptimized_X3' + 'ComputeOptimized_X350' + 'ComputeOptimized_X5' + 'ComputeOptimized_X50' + 'ComputeOptimized_X500' + 'ComputeOptimized_X700' + 'Enterprise_E1' + 'Enterprise_E10' + 'Enterprise_E100' + 'Enterprise_E20' + 'Enterprise_E200' + 'Enterprise_E400' + 'Enterprise_E5' + 'Enterprise_E50' + 'EnterpriseFlash_F1500' + 'EnterpriseFlash_F300' + 'EnterpriseFlash_F700' + 'FlashOptimized_A1000' + 'FlashOptimized_A1500' + 'FlashOptimized_A2000' + 'FlashOptimized_A250' + 'FlashOptimized_A4500' + 'FlashOptimized_A500' + 'FlashOptimized_A700' + 'MemoryOptimized_M10' + 'MemoryOptimized_M100' + 'MemoryOptimized_M1000' + 'MemoryOptimized_M150' + 'MemoryOptimized_M1500' + 'MemoryOptimized_M20' + 'MemoryOptimized_M2000' + 'MemoryOptimized_M250' + 'MemoryOptimized_M350' + 'MemoryOptimized_M50' + 'MemoryOptimized_M500' + 'MemoryOptimized_M700' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `zones` + +The Availability Zones to place the resources in. Currently only supported on Enterprise and EnterpriseFlash SKUs. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + 1 + 2 + 3 + ] + ``` +- Allowed: + ```Bicep + [ + 1 + 2 + 3 + ] + ``` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `databaseName` | string | The name of the Redis database. | +| `databaseResourceId` | string | The resource ID of the database. | +| `endpoint` | string | The Redis endpoint. | +| `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 Redis cluster. | +| `privateEndpoints` | array | The private endpoints of the Redis resource. | +| `resourceGroupName` | string | The name of the resource group the Redis resource was created in. | +| `resourceId` | string | The resource ID of the Redis cluster. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/res/network/private-endpoint:0.10.1` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/cache/redis-enterprise/database/README.md b/avm/res/cache/redis-enterprise/database/README.md new file mode 100644 index 0000000000..28382a40a4 --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/README.md @@ -0,0 +1,521 @@ +# Redis database `[Microsoft.Cache/redisEnterprise/databases]` + +This module deploys a Redis database in a Redis Enterprise or Azure Managed Redis (Preview) cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Cache/redisEnterprise/databases` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise/databases) | +| `Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise/databases/accessPolicyAssignments) | +| `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) | + +## Parameters + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`redisClusterName`](#parameter-redisclustername) | string | The name of the parent Redis Enterprise or Azure Managed Redis (Preview) resource. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessKeysAuthentication`](#parameter-accesskeysauthentication) | string | Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION. | +| [`accessPolicyAssignments`](#parameter-accesspolicyassignments) | array | Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION. | +| [`clientProtocol`](#parameter-clientprotocol) | string | Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols. | +| [`clusteringPolicy`](#parameter-clusteringpolicy) | string | Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering). | +| [`deferUpgrade`](#parameter-deferupgrade) | string | Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades). | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The database-level diagnostic settings of the service. | +| [`evictionPolicy`](#parameter-evictionpolicy) | string | Specifies the eviction policy for the Redis resource. | +| [`geoReplication`](#parameter-georeplication) | object | The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration. | +| [`modules`](#parameter-modules) | array | Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules). | +| [`name`](#parameter-name) | string | Name of the database. | +| [`persistence`](#parameter-persistence) | object | The persistence settings of the service. | +| [`port`](#parameter-port) | int | TCP port of the database endpoint. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | + +### Parameter: `redisClusterName` + +The name of the parent Redis Enterprise or Azure Managed Redis (Preview) resource. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `accessKeysAuthentication` + +Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `accessPolicyAssignments` + +Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userObjectId`](#parameter-accesspolicyassignmentsuserobjectid) | string | Object ID to which the access policy will be assigned. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessPolicyName`](#parameter-accesspolicyassignmentsaccesspolicyname) | string | Name of the access policy to be assigned. The current only allowed name is 'default'. | +| [`name`](#parameter-accesspolicyassignmentsname) | string | Name of the access policy assignment. | + +### Parameter: `accessPolicyAssignments.userObjectId` + +Object ID to which the access policy will be assigned. + +- Required: Yes +- Type: string + +### Parameter: `accessPolicyAssignments.accessPolicyName` + +Name of the access policy to be assigned. The current only allowed name is 'default'. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `accessPolicyAssignments.name` + +Name of the access policy assignment. + +- Required: No +- Type: string + +### Parameter: `clientProtocol` + +Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols. + +- Required: No +- Type: string +- Default: `'Encrypted'` +- Allowed: + ```Bicep + [ + 'Encrypted' + 'Plaintext' + ] + ``` + +### Parameter: `clusteringPolicy` + +Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering). + +- Required: No +- Type: string +- Default: `'OSSCluster'` +- Allowed: + ```Bicep + [ + 'EnterpriseCluster' + 'OSSCluster' + ] + ``` + +### Parameter: `deferUpgrade` + +Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades). + +- Required: No +- Type: string +- Default: `'NotDeferred'` +- Allowed: + ```Bicep + [ + 'Deferred' + 'NotDeferred' + ] + ``` + +### Parameter: `diagnosticSettings` + +The database-level diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `evictionPolicy` + +Specifies the eviction policy for the Redis resource. + +- Required: No +- Type: string +- Default: `'VolatileLRU'` +- Allowed: + ```Bicep + [ + 'AllKeysLFU' + 'AllKeysLRU' + 'AllKeysRandom' + 'NoEviction' + 'VolatileLFU' + 'VolatileLRU' + 'VolatileRandom' + 'VolatileTTL' + ] + ``` + +### Parameter: `geoReplication` + +The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupNickname`](#parameter-georeplicationgroupnickname) | string | The name of the geo-replication group. | +| [`linkedDatabases`](#parameter-georeplicationlinkeddatabases) | array | List of database resources to link with this database, including itself. | + +### Parameter: `geoReplication.groupNickname` + +The name of the geo-replication group. + +- Required: Yes +- Type: string + +### Parameter: `geoReplication.linkedDatabases` + +List of database resources to link with this database, including itself. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-georeplicationlinkeddatabasesid) | string | Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`. | + +### Parameter: `geoReplication.linkedDatabases.id` + +Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`. + +- Required: Yes +- Type: string + +### Parameter: `modules` + +Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules). + +- Required: No +- Type: array +- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-modulesname) | string | The name of the module. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`args`](#parameter-modulesargs) | string | Additional module arguments. | + +### Parameter: `modules.name` + +The name of the module. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'RedisBloom' + 'RediSearch' + 'RedisJSON' + 'RedisTimeSeries' + ] + ``` + +### Parameter: `modules.args` + +Additional module arguments. + +- Required: No +- Type: string + +### Parameter: `name` + +Name of the database. + +- Required: No +- Type: string +- Default: `'default'` +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `persistence` + +The persistence settings of the service. + +- Required: No +- Type: object +- Default: + ```Bicep + { + type: 'disabled' + } + ``` + +### Parameter: `port` + +TCP port of the database endpoint. + +- Required: No +- Type: int +- Default: `10000` +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object +- MinValue: 10000 +- MaxValue: 10000 + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The resource ID of the key vault where to store the secrets of this module. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`primaryAccessKeyName`](#parameter-secretsexportconfigurationprimaryaccesskeyname) | string | The primaryAccessKey secret name to create. | +| [`primaryConnectionStringName`](#parameter-secretsexportconfigurationprimaryconnectionstringname) | string | The primaryConnectionString secret name to create. | +| [`primaryStackExchangeRedisConnectionStringName`](#parameter-secretsexportconfigurationprimarystackexchangeredisconnectionstringname) | string | The primaryStackExchangeRedisConnectionString secret name to create. | +| [`secondaryAccessKeyName`](#parameter-secretsexportconfigurationsecondaryaccesskeyname) | string | The secondaryAccessKey secret name to create. | +| [`secondaryConnectionStringName`](#parameter-secretsexportconfigurationsecondaryconnectionstringname) | string | The secondaryConnectionString secret name to create. | +| [`secondaryStackExchangeRedisConnectionStringName`](#parameter-secretsexportconfigurationsecondarystackexchangeredisconnectionstringname) | string | The secondaryStackExchangeRedisConnectionString secret name to create. | + +### Parameter: `secretsExportConfiguration.keyVaultResourceId` + +The resource ID of the key vault where to store the secrets of this module. + +- Required: Yes +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.primaryAccessKeyName` + +The primaryAccessKey secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.primaryConnectionStringName` + +The primaryConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.primaryStackExchangeRedisConnectionStringName` + +The primaryStackExchangeRedisConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.secondaryAccessKeyName` + +The secondaryAccessKey secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.secondaryConnectionStringName` + +The secondaryConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +### Parameter: `secretsExportConfiguration.secondaryStackExchangeRedisConnectionStringName` + +The secondaryStackExchangeRedisConnectionString secret name to create. + +- Required: No +- Type: string +- MinValue: 10000 +- MaxValue: 10000 + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The Redis endpoint. | +| `exportedSecrets` | | A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name. | +| `name` | string | The name of the Redis database. | +| `port` | int | The Redis database port. | +| `resourceGroupName` | string | The name of the resource group the Redis resource was created in. | +| `resourceId` | string | The resource ID of the database. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | diff --git a/avm/res/cache/redis-enterprise/database/access-policy-assignment/README.md b/avm/res/cache/redis-enterprise/database/access-policy-assignment/README.md new file mode 100644 index 0000000000..dea6191c23 --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/access-policy-assignment/README.md @@ -0,0 +1,88 @@ +# Azure Managed Redis (Preview) Database Access Policy Assignment `[Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments]` + +This module deploys an access policy assignment for an Azure Managed Redis (Preview) database. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments` | [2024-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/2024-09-01-preview/redisEnterprise/databases/accessPolicyAssignments) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userObjectId`](#parameter-userobjectid) | string | Object ID to which the access policy will be assigned. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`clusterName`](#parameter-clustername) | string | The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment. | +| [`databaseName`](#parameter-databasename) | string | The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessPolicyName`](#parameter-accesspolicyname) | string | Name of the access policy to be assigned. | +| [`name`](#parameter-name) | string | Name of the access policy assignment. | + +### Parameter: `userObjectId` + +Object ID to which the access policy will be assigned. + +- Required: Yes +- Type: string + +### Parameter: `clusterName` + +The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `databaseName` + +The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `accessPolicyName` + +Name of the access policy to be assigned. + +- Required: No +- Type: string +- Default: `'default'` +- Allowed: + ```Bicep + [ + 'default' + ] + ``` + +### Parameter: `name` + +Name of the access policy assignment. + +- Required: No +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the access policy assignment. | +| `resourceGroupName` | string | The resource group the access policy assignment was deployed into. | +| `resourceId` | string | The resource ID of the access policy assignment. | +| `userObjectId` | string | The object ID of the user associated with the access policy. | diff --git a/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.bicep b/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.bicep new file mode 100644 index 0000000000..87c5f1905e --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.bicep @@ -0,0 +1,51 @@ +metadata name = 'Azure Managed Redis (Preview) Database Access Policy Assignment' +metadata description = 'This module deploys an access policy assignment for an Azure Managed Redis (Preview) database.' + +@description('Optional. Name of the access policy assignment.') +param name string? + +@description('Required. Object ID to which the access policy will be assigned.') +param userObjectId string + +@description('Conditional. The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment.') +param clusterName string + +@description('Conditional. The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment.') +param databaseName string + +@allowed([ + 'default' +]) +@description('Optional. Name of the access policy to be assigned.') +param accessPolicyName string = 'default' + +resource redisCluster 'Microsoft.Cache/redisEnterprise@2024-09-01-preview' existing = { + name: clusterName + + resource database 'databases@2024-09-01-preview' existing = { + name: databaseName + } +} + +resource accessPolicyAssignment 'Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments@2024-09-01-preview' = { + name: name ?? userObjectId + parent: redisCluster::database + properties: { + accessPolicyName: accessPolicyName + user: { + objectId: userObjectId + } + } +} + +@description('The name of the access policy assignment.') +output name string = accessPolicyAssignment.name + +@description('The resource ID of the access policy assignment.') +output resourceId string = accessPolicyAssignment.id + +@description('The resource group the access policy assignment was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The object ID of the user associated with the access policy.') +output userObjectId string = accessPolicyAssignment.properties.user.objectId diff --git a/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.json b/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.json new file mode 100644 index 0000000000..3e7603c8b8 --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/access-policy-assignment/main.json @@ -0,0 +1,106 @@ +{ + "$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.33.93.31351", + "templateHash": "6473026260124919331" + }, + "name": "Azure Managed Redis (Preview) Database Access Policy Assignment", + "description": "This module deploys an access policy assignment for an Azure Managed Redis (Preview) database." + }, + "parameters": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "clusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment." + } + }, + "accessPolicyName": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. Name of the access policy to be assigned." + } + } + }, + "resources": { + "redisCluster::database": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise/databases", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('databaseName'))]" + }, + "redisCluster": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('clusterName')]" + }, + "accessPolicyAssignment": { + "type": "Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]", + "properties": { + "accessPolicyName": "[parameters('accessPolicyName')]", + "user": { + "objectId": "[parameters('userObjectId')]" + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policy assignment." + }, + "value": "[coalesce(parameters('name'), parameters('userObjectId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policy assignment." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the access policy assignment was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "The object ID of the user associated with the access policy." + }, + "value": "[reference('accessPolicyAssignment').user.objectId]" + } + } +} diff --git a/avm/res/cache/redis-enterprise/database/main.bicep b/avm/res/cache/redis-enterprise/database/main.bicep new file mode 100644 index 0000000000..3a45317109 --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/main.bicep @@ -0,0 +1,329 @@ +metadata name = 'Redis database' +metadata description = 'This module deploys a Redis database in a Redis Enterprise or Azure Managed Redis (Preview) cluster.' + +@description('Conditional. The name of the parent Redis Enterprise or Azure Managed Redis (Preview) resource. Required if the template is used in a standalone deployment.') +param redisClusterName string + +@allowed([ + 'default' +]) +@description('Optional. Name of the database.') +param name string = 'default' + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION.') +param accessKeysAuthentication string = 'Enabled' + +@allowed([ + 'Encrypted' + 'Plaintext' +]) +@description('Optional. Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols.') +param clientProtocol string = 'Encrypted' + +@allowed([ + 'EnterpriseCluster' + 'OSSCluster' +]) +@description('Optional. Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering).') +param clusteringPolicy string = 'OSSCluster' + +@allowed([ + 'Deferred' + 'NotDeferred' +]) +@description('Optional. Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades).') +param deferUpgrade string = 'NotDeferred' + +@allowed([ + 'AllKeysLFU' + 'AllKeysLRU' + 'AllKeysRandom' + 'NoEviction' + 'VolatileLFU' + 'VolatileLRU' + 'VolatileRandom' + 'VolatileTTL' +]) +@description('Optional. Specifies the eviction policy for the Redis resource.') +param evictionPolicy string = 'VolatileLRU' + +@description('Optional. The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration.') +param geoReplication geoReplicationType? + +@description('Optional. Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules).') +param modules moduleType[] = [] + +@description('Optional. TCP port of the database endpoint.') +@minValue(10000) +@maxValue(10000) +param port int = 10000 + +@description('Optional. The persistence settings of the service.') +param persistence persistenceType = { + type: 'disabled' +} + +@description('Optional. Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION.') +param accessPolicyAssignments accessPolicyAssignmentType[]? + +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? + +import { diagnosticSettingLogsOnlyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The database-level diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingLogsOnlyType[]? + +// ============== // +// Resources // +// ============== // + +resource redisCluster 'Microsoft.Cache/redisEnterprise@2024-09-01-preview' existing = { + name: redisClusterName +} + +var clusterSku = redisCluster.sku.name +var isAmr = startsWith(clusterSku, 'Balanced') || startsWith(clusterSku, 'ComputeOptimized') || startsWith( + clusterSku, + 'FlashOptimized' +) || startsWith(clusterSku, 'MemoryOptimized') + +resource redisDatabase 'Microsoft.Cache/redisEnterprise/databases@2024-09-01-preview' = { + parent: redisCluster + name: name + properties: { + accessKeysAuthentication: isAmr ? accessKeysAuthentication : null + clientProtocol: clientProtocol + clusteringPolicy: clusteringPolicy + deferUpgrade: deferUpgrade + evictionPolicy: evictionPolicy + geoReplication: !empty(geoReplication) ? geoReplication : null + modules: modules + port: port + persistence: persistence.type != 'disabled' + ? { + aofEnabled: persistence.type == 'aof' + rdbEnabled: persistence.type == 'rdb' + aofFrequency: persistence.type == 'aof' ? persistence.frequency : null + rdbFrequency: persistence.type == 'rdb' ? persistence.frequency : null + } + : null + } +} + +module database_accessPolicyAssignments 'access-policy-assignment/main.bicep' = [ + for (assignment, index) in (accessPolicyAssignments ?? []): { + name: '${uniqueString(deployment().name)}-redis-apa-${index}' + params: { + name: assignment.name + clusterName: redisCluster.name + databaseName: redisDatabase.name + accessPolicyName: assignment.?accessPolicyName + userObjectId: assignment.userObjectId + } + } +] + +resource database_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: redisDatabase + } +] + +module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) { + name: '${uniqueString(deployment().name)}-secrets-kv' + scope: resourceGroup( + split(secretsExportConfiguration.?keyVaultResourceId!, '/')[2], + split(secretsExportConfiguration.?keyVaultResourceId!, '/')[4] + ) + params: { + keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId!, '/')) + secretsToSet: union( + [], + contains(secretsExportConfiguration!, 'primaryAccessKeyName') + ? [ + { + name: secretsExportConfiguration!.?primaryAccessKeyName + value: redisDatabase.listKeys().primaryKey + } + ] + : [], + contains(secretsExportConfiguration!, 'primaryConnectionStringName') + ? [ + { + name: secretsExportConfiguration!.?primaryConnectionStringName + value: '${redisDatabase.properties.clientProtocol == 'Plaintext' ? 'redis://' : 'rediss://' }:${redisDatabase.listKeys().primaryKey}@${redisCluster.properties.hostName}:${redisDatabase.properties.port}' + } + ] + : [], + contains(secretsExportConfiguration!, 'primaryStackExchangeRedisConnectionStringName') + ? [ + { + name: secretsExportConfiguration!.?primaryStackExchangeRedisConnectionStringName + value: '${redisCluster.properties.hostName}:${redisDatabase.properties.port},password=${redisDatabase.listKeys().primaryKey},ssl=${redisDatabase.properties.clientProtocol == 'Plaintext' ? 'False' : 'True'},abortConnect=False' + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryAccessKeyName') + ? [ + { + name: secretsExportConfiguration!.?secondaryAccessKeyName + value: redisDatabase.listKeys().secondaryKey + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryConnectionStringName') + ? [ + { + name: secretsExportConfiguration!.?secondaryConnectionStringName + value: '${redisDatabase.properties.clientProtocol == 'Plaintext' ? 'redis://' : 'rediss://' }:${redisDatabase.listKeys().secondaryKey}@${redisCluster.properties.hostName}:${redisDatabase.properties.port}' + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryStackExchangeRedisConnectionStringName') + ? [ + { + name: secretsExportConfiguration!.?secondaryStackExchangeRedisConnectionStringName + value: '${redisCluster.properties.hostName}:${redisDatabase.properties.port},password=${redisDatabase.listKeys().secondaryKey},ssl=${redisDatabase.properties.clientProtocol == 'Plaintext' ? 'False' : 'True'},abortConnect=False' + } + ] + : [] + ) + } +} + +// ============ // +// Outputs // +// ============ // + +@description('The name of the Redis database.') +output name string = redisDatabase.name + +@description('The resource ID of the database.') +output resourceId string = redisDatabase.id + +@description('The name of the resource group the Redis resource was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The Redis database port.') +output port int = redisDatabase.properties.port + +@description('The Redis endpoint.') +output endpoint string = '${redisCluster.properties.hostName}:${redisDatabase.properties.port}' + +import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@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 // +// =============== // + +@export() +type secretsExportConfigurationType = { + @description('Required. The resource ID of the key vault where to store the secrets of this module.') + keyVaultResourceId: string + + @description('Optional. The primaryAccessKey secret name to create.') + primaryAccessKeyName: string? + + @description('Optional. The primaryConnectionString secret name to create.') + primaryConnectionStringName: string? + + @description('Optional. The primaryStackExchangeRedisConnectionString secret name to create.') + primaryStackExchangeRedisConnectionStringName: string? + + @description('Optional. The secondaryAccessKey secret name to create.') + secondaryAccessKeyName: string? + + @description('Optional. The secondaryConnectionString secret name to create.') + secondaryConnectionStringName: string? + + @description('Optional. The secondaryStackExchangeRedisConnectionString secret name to create.') + secondaryStackExchangeRedisConnectionStringName: string? +} + +@export() +type disabledPersistenceType = { + @description('Required. Disabled persistence type.') + type: 'disabled' +} + +@export() +type aofPersistenceType = { + @description('Required. AOF persistence type.') + type: 'aof' + + @description('Required. The frequency at which data is written to disk.') + frequency: '1s' +} + +@export() +type rdbPersistenceType = { + @description('Required. RDB persistence type.') + type: 'rdb' + + @description('Required. The frequency at which an RDB snapshot of the database is created.') + frequency: '1h' | '6h' | '12h' +} + +@export() +@discriminator('type') +type persistenceType = disabledPersistenceType | aofPersistenceType | rdbPersistenceType + +@export() +type moduleType = { + @description('Required. The name of the module.') + name: ('RedisBloom' | 'RedisTimeSeries' | 'RedisJSON' | 'RediSearch') + + @description('Optional. Additional module arguments.') + args: string? +} + +@export() +type geoReplicationType = { + @description('Required. The name of the geo-replication group.') + groupNickname: string + + @description('Required. List of database resources to link with this database, including itself.') + linkedDatabases: linkedDatabaseType[] +} + +@export() +type linkedDatabaseType = { + @description('Required. Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`.') + id: string +} + +@export() +type accessPolicyAssignmentType = { + @description('Optional. Name of the access policy assignment.') + name: string? + + @description('Required. Object ID to which the access policy will be assigned.') + userObjectId: string + + @description('Optional. Name of the access policy to be assigned. The current only allowed name is \'default\'.') + accessPolicyName: ('default')? +} diff --git a/avm/res/cache/redis-enterprise/database/main.json b/avm/res/cache/redis-enterprise/database/main.json new file mode 100644 index 0000000000..82a2cc12c4 --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/main.json @@ -0,0 +1,933 @@ +{ + "$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.33.93.31351", + "templateHash": "12094637252334328193" + }, + "name": "Redis database", + "description": "This module deploys a Redis database in a Redis Enterprise or Azure Managed Redis (Preview) cluster." + }, + "definitions": { + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "primaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAccessKey secret name to create." + } + }, + "primaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryConnectionString secret name to create." + } + }, + "primaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryStackExchangeRedisConnectionString secret name to create." + } + }, + "secondaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAccessKey secret name to create." + } + }, + "secondaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryConnectionString secret name to create." + } + }, + "secondaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryStackExchangeRedisConnectionString secret name to create." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "disabledPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "disabled" + ], + "metadata": { + "description": "Required. Disabled persistence type." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "aofPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "aof" + ], + "metadata": { + "description": "Required. AOF persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "1s" + ], + "metadata": { + "description": "Required. The frequency at which data is written to disk." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "rdbPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "rdb" + ], + "metadata": { + "description": "Required. RDB persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "12h", + "1h", + "6h" + ], + "metadata": { + "description": "Required. The frequency at which an RDB snapshot of the database is created." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "persistenceType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "disabled": { + "$ref": "#/definitions/disabledPersistenceType" + }, + "aof": { + "$ref": "#/definitions/aofPersistenceType" + }, + "rdb": { + "$ref": "#/definitions/rdbPersistenceType" + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "moduleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "RediSearch", + "RedisBloom", + "RedisJSON", + "RedisTimeSeries" + ], + "metadata": { + "description": "Required. The name of the module." + } + }, + "args": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Additional module arguments." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "geoReplicationType": { + "type": "object", + "properties": { + "groupNickname": { + "type": "string", + "metadata": { + "description": "Required. The name of the geo-replication group." + } + }, + "linkedDatabases": { + "type": "array", + "items": { + "$ref": "#/definitions/linkedDatabaseType" + }, + "metadata": { + "description": "Required. List of database resources to link with this database, including itself." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "linkedDatabaseType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "accessPolicyAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "accessPolicyName": { + "type": "string", + "allowedValues": [ + "default" + ], + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy to be assigned. The current only allowed name is 'default'." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.secretSetOutputType": { + "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." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingLogsOnlyType": { + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "redisClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Redis Enterprise or Azure Managed Redis (Preview) resource. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. Name of the database." + } + }, + "accessKeysAuthentication": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION." + } + }, + "clientProtocol": { + "type": "string", + "defaultValue": "Encrypted", + "allowedValues": [ + "Encrypted", + "Plaintext" + ], + "metadata": { + "description": "Optional. Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols." + } + }, + "clusteringPolicy": { + "type": "string", + "defaultValue": "OSSCluster", + "allowedValues": [ + "EnterpriseCluster", + "OSSCluster" + ], + "metadata": { + "description": "Optional. Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering)." + } + }, + "deferUpgrade": { + "type": "string", + "defaultValue": "NotDeferred", + "allowedValues": [ + "Deferred", + "NotDeferred" + ], + "metadata": { + "description": "Optional. Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades)." + } + }, + "evictionPolicy": { + "type": "string", + "defaultValue": "VolatileLRU", + "allowedValues": [ + "AllKeysLFU", + "AllKeysLRU", + "AllKeysRandom", + "NoEviction", + "VolatileLFU", + "VolatileLRU", + "VolatileRandom", + "VolatileTTL" + ], + "metadata": { + "description": "Optional. Specifies the eviction policy for the Redis resource." + } + }, + "geoReplication": { + "$ref": "#/definitions/geoReplicationType", + "nullable": true, + "metadata": { + "description": "Optional. The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration." + } + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/definitions/moduleType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules)." + } + }, + "port": { + "type": "int", + "defaultValue": 10000, + "minValue": 10000, + "maxValue": 10000, + "metadata": { + "description": "Optional. TCP port of the database endpoint." + } + }, + "persistence": { + "$ref": "#/definitions/persistenceType", + "defaultValue": { + "type": "disabled" + }, + "metadata": { + "description": "Optional. The persistence settings of the service." + } + }, + "accessPolicyAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/accessPolicyAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The database-level diagnostic settings of the service." + } + } + }, + "resources": { + "redisCluster": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('redisClusterName')]" + }, + "redisDatabase": { + "type": "Microsoft.Cache/redisEnterprise/databases", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}', parameters('redisClusterName'), parameters('name'))]", + "properties": { + "accessKeysAuthentication": "[if(or(or(or(startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'Balanced'), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'ComputeOptimized')), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'FlashOptimized')), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'MemoryOptimized')), parameters('accessKeysAuthentication'), null())]", + "clientProtocol": "[parameters('clientProtocol')]", + "clusteringPolicy": "[parameters('clusteringPolicy')]", + "deferUpgrade": "[parameters('deferUpgrade')]", + "evictionPolicy": "[parameters('evictionPolicy')]", + "geoReplication": "[if(not(empty(parameters('geoReplication'))), parameters('geoReplication'), null())]", + "modules": "[parameters('modules')]", + "port": "[parameters('port')]", + "persistence": "[if(not(equals(parameters('persistence').type, 'disabled')), createObject('aofEnabled', equals(parameters('persistence').type, 'aof'), 'rdbEnabled', equals(parameters('persistence').type, 'rdb'), 'aofFrequency', if(equals(parameters('persistence').type, 'aof'), parameters('persistence').frequency, null()), 'rdbFrequency', if(equals(parameters('persistence').type, 'rdb'), parameters('persistence').frequency, null())), null())]" + }, + "dependsOn": [ + "redisCluster" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Cache/redisEnterprise/{0}/databases/{1}', parameters('redisClusterName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "redisDatabase" + ] + }, + "database_accessPolicyAssignments": { + "copy": { + "name": "database_accessPolicyAssignments", + "count": "[length(coalesce(parameters('accessPolicyAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-redis-apa-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()].name]" + }, + "clusterName": { + "value": "[parameters('redisClusterName')]" + }, + "databaseName": { + "value": "[parameters('name')]" + }, + "accessPolicyName": { + "value": "[tryGet(coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()], 'accessPolicyName')]" + }, + "userObjectId": { + "value": "[coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()].userObjectId]" + } + }, + "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.33.93.31351", + "templateHash": "6473026260124919331" + }, + "name": "Azure Managed Redis (Preview) Database Access Policy Assignment", + "description": "This module deploys an access policy assignment for an Azure Managed Redis (Preview) database." + }, + "parameters": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "clusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment." + } + }, + "accessPolicyName": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. Name of the access policy to be assigned." + } + } + }, + "resources": { + "redisCluster::database": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise/databases", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('databaseName'))]" + }, + "redisCluster": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('clusterName')]" + }, + "accessPolicyAssignment": { + "type": "Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]", + "properties": { + "accessPolicyName": "[parameters('accessPolicyName')]", + "user": { + "objectId": "[parameters('userObjectId')]" + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policy assignment." + }, + "value": "[coalesce(parameters('name'), parameters('userObjectId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policy assignment." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the access policy assignment was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "The object ID of the user associated with the access policy." + }, + "value": "[reference('accessPolicyAssignment').user.objectId]" + } + } + } + }, + "dependsOn": [ + "redisDatabase" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAccessKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryAccessKeyName'), 'value', listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryConnectionStringName'), 'value', format('{0}:{1}@{2}:{3}', if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'redis://', 'rediss://'), listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey, reference('redisCluster').hostName, reference('redisDatabase').port))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryStackExchangeRedisConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryStackExchangeRedisConnectionStringName'), 'value', format('{0}:{1},password={2},ssl={3},abortConnect=False', reference('redisCluster').hostName, reference('redisDatabase').port, listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey, if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'False', 'True')))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAccessKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryAccessKeyName'), 'value', listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryConnectionStringName'), 'value', format('{0}:{1}@{2}:{3}', if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'redis://', 'rediss://'), listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey, reference('redisCluster').hostName, reference('redisDatabase').port))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryStackExchangeRedisConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryStackExchangeRedisConnectionStringName'), 'value', format('{0}:{1},password={2},ssl={3},abortConnect=False', reference('redisCluster').hostName, reference('redisDatabase').port, listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey, if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'False', 'True')))), 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.33.93.31351", + "templateHash": "8063348652715653257" + } + }, + "definitions": { + "secretSetOutputType": { + "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." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the secrets 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]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "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]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "redisCluster", + "redisDatabase" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Redis database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the database." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Redis resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "port": { + "type": "int", + "metadata": { + "description": "The Redis database port." + }, + "value": "[reference('redisDatabase').port]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The Redis endpoint." + }, + "value": "[format('{0}:{1}', reference('redisCluster').hostName, reference('redisDatabase').port)]" + }, + "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())]" + } + } +} diff --git a/avm/res/cache/redis-enterprise/database/modules/keyVaultExport.bicep b/avm/res/cache/redis-enterprise/database/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..173266421b --- /dev/null +++ b/avm/res/cache/redis-enterprise/database/modules/keyVaultExport.bicep @@ -0,0 +1,43 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Key Vault to set the secrets in.') +param keyVaultName string + +import { secretToSetType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@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 // +// =========== // + +import { secretSetOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetOutputType[] = [ + #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 + secretUriWithVersion: secrets[index].properties.secretUriWithVersion + } +] diff --git a/avm/res/cache/redis-enterprise/main.bicep b/avm/res/cache/redis-enterprise/main.bicep new file mode 100644 index 0000000000..be6a15d848 --- /dev/null +++ b/avm/res/cache/redis-enterprise/main.bicep @@ -0,0 +1,517 @@ +metadata name = 'Redis Enterprise and Azure Managed Redis (Preview)' +metadata description = 'This module deploys a Redis Enterprise or Azure Managed Redis (Preview) cache.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The name of the cache resource.') +param name string + +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The lock settings of the service.') +param lock lockType? + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +@description('Optional. Tags of the resource.') +param tags object? + +import { managedIdentityOnlyUserAssignedType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Conditional. The managed identity definition for this resource. Required if \'customerManagedKey\' is not empty.') +param managedIdentities managedIdentityOnlyUserAssignedType? + +import { customerManagedKeyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The customer managed key definition to use for the managed service.') +param customerManagedKey customerManagedKeyType? + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. Specifies whether to enable data replication for high availability. Used only with Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. HIGH AVAILABILITY IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-high-availability) FOR CLARIFICATION.') +param highAvailability string = 'Enabled' + +@allowed([ + '1.2' +]) +@description('Optional. The minimum TLS version for the Redis cluster to support.') +param minimumTlsVersion string = '1.2' + +@allowed([ + 2 + 3 + 4 + 6 + 8 + 9 + 10 +]) +@description('Optional. The size of the cluster. Only supported on Redis Enterprise SKUs: Enterprise, EnterpriseFlash. Valid values are (2, 4, 6, 8, 10) for Enterprise SKUs and (3, 9) for EnterpriseFlash SKUs. [Learn more](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-enterprise-tiers#sharding-and-cpu-utilization).') +param capacity int = 2 + +@allowed([ + 'Balanced_B0' + 'Balanced_B1' + 'Balanced_B3' + 'Balanced_B5' + 'Balanced_B10' + 'Balanced_B20' + 'Balanced_B50' + 'Balanced_B100' + 'Balanced_B150' + 'Balanced_B250' + 'Balanced_B350' + 'Balanced_B500' + 'Balanced_B700' + 'Balanced_B1000' + 'ComputeOptimized_X3' + 'ComputeOptimized_X5' + 'ComputeOptimized_X10' + 'ComputeOptimized_X20' + 'ComputeOptimized_X50' + 'ComputeOptimized_X100' + 'ComputeOptimized_X150' + 'ComputeOptimized_X250' + 'ComputeOptimized_X350' + 'ComputeOptimized_X500' + 'ComputeOptimized_X700' + 'Enterprise_E1' + 'Enterprise_E5' + 'Enterprise_E10' + 'Enterprise_E20' + 'Enterprise_E50' + 'Enterprise_E100' + 'Enterprise_E200' + 'Enterprise_E400' + 'EnterpriseFlash_F300' + 'EnterpriseFlash_F700' + 'EnterpriseFlash_F1500' + 'FlashOptimized_A250' + 'FlashOptimized_A500' + 'FlashOptimized_A700' + 'FlashOptimized_A1000' + 'FlashOptimized_A1500' + 'FlashOptimized_A2000' + 'FlashOptimized_A4500' + 'MemoryOptimized_M10' + 'MemoryOptimized_M20' + 'MemoryOptimized_M50' + 'MemoryOptimized_M100' + 'MemoryOptimized_M150' + 'MemoryOptimized_M250' + 'MemoryOptimized_M350' + 'MemoryOptimized_M500' + 'MemoryOptimized_M700' + 'MemoryOptimized_M1000' + 'MemoryOptimized_M1500' + 'MemoryOptimized_M2000' +]) +@description('Optional. The type of cluster to deploy. Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized ARE IN PREVIEW, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview#tiers-and-skus-at-a-glance) FOR CLARIFICATION.') +param skuName string = 'Enterprise_E5' + +@allowed([ + 1 + 2 + 3 +]) +@description('Optional. The Availability Zones to place the resources in. Currently only supported on Enterprise and EnterpriseFlash SKUs.') +param zones int[] = [ + 1 + 2 + 3 +] + +// ================ // +// Database params // +// ================ // +@description('Optional. Database configuration.') +param database databaseType? + +// ============ // +// Other params // +// ============ // + +import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointSingleServiceType[]? + +import { diagnosticSettingMetricsOnlyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The cluster-level diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingMetricsOnlyType[]? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var isAmr = startsWith(skuName, 'Balanced') || startsWith(skuName, 'ComputeOptimized') || startsWith( + skuName, + 'FlashOptimized' +) || startsWith(skuName, 'MemoryOptimized') +var isEnterprise = startsWith(skuName, 'Enterprise') || startsWith(skuName, 'EnterpriseFlash') + +var availabilityZones = isEnterprise ? map(zones, zone => string(zone)) : [] + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) + ? { + type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None' + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +var builtInRoleNames = { + 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') + 'Redis Cache Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'e0f68234-74aa-48ed-b826-c38b57376e17' + ) + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.cache-redisenterprise.${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!, '/')) + 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! + } +} + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId!, '/')) + scope: resourceGroup( + split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[2], + split(customerManagedKey.?userAssignedIdentityResourceId!, '/')[4] + ) +} + +resource redisCluster 'Microsoft.Cache/redisEnterprise@2024-09-01-preview' = { + name: name + location: location + tags: tags + identity: identity + properties: { + encryption: !empty(customerManagedKey) + ? { + customerManagedKeyEncryption: { + keyEncryptionKeyIdentity: !empty(customerManagedKey.?userAssignedIdentityResourceId) + ? { + identityType: 'userAssignedIdentity' + userAssignedIdentityResourceId: cMKUserAssignedIdentity.id + } + : null + keyEncryptionKeyUrl: !empty(customerManagedKey.?keyVersion ?? '') + ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.?keyVersion}' + : cMKKeyVault::cMKKey.properties.keyUriWithVersion + } + } + : null + highAvailability: isAmr ? highAvailability : null + minimumTlsVersion: minimumTlsVersion + } + sku: { + capacity: isEnterprise ? capacity : null + name: skuName + } + zones: !empty(availabilityZones) ? availabilityZones : null +} + +module redisCluster_database 'database/main.bicep' = { + name: '${uniqueString(deployment().name, location)}-redis-database' + params: { + name: database.?name + redisClusterName: redisCluster.name + accessKeysAuthentication: isAmr ? database.?accessKeysAuthentication : null + accessPolicyAssignments: isAmr ? database.?accessPolicyAssignments : null + clientProtocol: database.?clientProtocol + clusteringPolicy: database.?clusteringPolicy + deferUpgrade: database.?deferUpgrade + evictionPolicy: database.?evictionPolicy + geoReplication: database.?geoReplication + modules: database.?modules + port: database.?port + persistence: database.?persistence + secretsExportConfiguration: database.?secretsExportConfiguration + diagnosticSettings: database.?diagnosticSettings + } +} + +resource redisCluster_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: redisCluster +} + +resource redisCluster_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: redisCluster + } +] + +resource redisCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(redisCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: redisCluster + } +] + +module redisEnterprise_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.10.1' = [ + for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-redisEnterprise-PrivateEndpoint-${index}' + scope: resourceGroup( + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2], + split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4] + ) + params: { + name: privateEndpoint.?name ?? 'pep-${last(split(redisCluster.id, '/'))}-${privateEndpoint.?service ?? 'redisEnterprise'}-${index}' + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(redisCluster.id, '/'))}-${privateEndpoint.?service ?? 'redisEnterprise'}-${index}' + properties: { + privateLinkServiceId: redisCluster.id + groupIds: [ + privateEndpoint.?service ?? 'redisEnterprise' + ] + } + } + ] + : null + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(redisCluster.id, '/'))}-${privateEndpoint.?service ?? 'redisEnterprise'}-${index}' + properties: { + privateLinkServiceId: redisCluster.id + groupIds: [ + privateEndpoint.?service ?? 'redisEnterprise' + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] + : null + subnetResourceId: privateEndpoint.subnetResourceId + enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry + location: privateEndpoint.?location ?? reference( + split(privateEndpoint.subnetResourceId, '/subnets/')[0], + '2020-06-01', + 'Full' + ).location + lock: privateEndpoint.?lock ?? lock + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup + roleAssignments: privateEndpoint.?roleAssignments + tags: privateEndpoint.?tags ?? tags + customDnsConfigs: privateEndpoint.?customDnsConfigs + ipConfigurations: privateEndpoint.?ipConfigurations + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName + } + } +] + +// ============ // +// Outputs // +// ============ // + +@description('The name of the Redis cluster.') +output name string = redisCluster.name + +@description('The resource ID of the Redis cluster.') +output resourceId string = redisCluster.id + +@description('The name of the Redis database.') +output databaseName string = redisCluster_database.outputs.name + +@description('The resource ID of the database.') +output databaseResourceId string = redisCluster_database.outputs.resourceId + +@description('The name of the resource group the Redis resource was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The Redis endpoint.') +output endpoint string = redisCluster_database.outputs.endpoint + +@description('The location the resource was deployed into.') +output location string = redisCluster.location + +@description('The private endpoints of the Redis resource.') +output privateEndpoints privateEndpointOutputType[] = [ + for (pe, index) in (privateEndpoints ?? []): { + name: redisEnterprise_privateEndpoints[index].outputs.name + resourceId: redisEnterprise_privateEndpoints[index].outputs.resourceId + groupId: redisEnterprise_privateEndpoints[index].outputs.?groupId! + customDnsConfigs: redisEnterprise_privateEndpoints[index].outputs.customDnsConfigs + networkInterfaceResourceIds: redisEnterprise_privateEndpoints[index].outputs.networkInterfaceResourceIds + } +] + +import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@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 = redisCluster_database.outputs.exportedSecrets + +// =============== // +// Definitions // +// =============== // + +import { diagnosticSettingLogsOnlyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +import { geoReplicationType, moduleType, persistenceType, accessPolicyAssignmentType, secretsExportConfigurationType } from 'database/main.bicep' + +@export() +type databaseType = { + @description('Optional. Name of the database.') + name: ('default')? + + @description('Optional. Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION.') + accessKeysAuthentication: ('Disabled' | 'Enabled')? + + @description('Optional. Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols.') + clientProtocol: ('Encrypted' | 'Plaintext')? + + @description('Optional. Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering).') + clusteringPolicy: ('EnterpriseCluster' | 'OSSCluster')? + + @description('Optional. Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades).') + deferUpgrade: ('Deferred' | 'NotDeferred')? + + @description('Optional. Specifies the eviction policy for the Redis resource.') + evictionPolicy: ( + | 'AllKeysLFU' + | 'AllKeysLRU' + | 'AllKeysRandom' + | 'NoEviction' + | 'VolatileLFU' + | 'VolatileLRU' + | 'VolatileRandom' + | 'VolatileTTL')? + + @description('Optional. The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration.') + geoReplication: geoReplicationType? + + @description('Optional. Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules).') + modules: moduleType[]? + + @description('Optional. TCP port of the database endpoint.') + @minValue(10000) + @maxValue(10000) + port: int? + + @description('Optional. The persistence settings of the service.') + persistence: persistenceType? + + @description('Optional. Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION.') + accessPolicyAssignments: accessPolicyAssignmentType[]? + + @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') + secretsExportConfiguration: secretsExportConfigurationType? + + @description('Optional. The database-level diagnostic settings of the service.') + diagnosticSettings: diagnosticSettingLogsOnlyType[]? +} + +@export() +type privateEndpointOutputType = { + @description('The name of the private endpoint.') + name: string + + @description('The resource ID of the private endpoint.') + resourceId: string + + @description('The group Id for the private endpoint Group.') + groupId: string? + + @description('The custom DNS configurations of the private endpoint.') + customDnsConfigs: { + @description('FQDN that resolves to private endpoint IP address.') + fqdn: string? + + @description('A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[] + + @description('The IDs of the network interfaces associated with the private endpoint.') + networkInterfaceResourceIds: string[] +} diff --git a/avm/res/cache/redis-enterprise/main.json b/avm/res/cache/redis-enterprise/main.json new file mode 100644 index 0000000000..56ee4d066e --- /dev/null +++ b/avm/res/cache/redis-enterprise/main.json @@ -0,0 +1,3312 @@ +{ + "$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.33.93.31351", + "templateHash": "11157703241699996924" + }, + "name": "Redis Enterprise and Azure Managed Redis (Preview)", + "description": "This module deploys a Redis Enterprise or Azure Managed Redis (Preview) cache.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "databaseType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "default" + ], + "nullable": true, + "metadata": { + "description": "Optional. Name of the database." + } + }, + "accessKeysAuthentication": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION." + } + }, + "clientProtocol": { + "type": "string", + "allowedValues": [ + "Encrypted", + "Plaintext" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols." + } + }, + "clusteringPolicy": { + "type": "string", + "allowedValues": [ + "EnterpriseCluster", + "OSSCluster" + ], + "nullable": true, + "metadata": { + "description": "Optional. Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering)." + } + }, + "deferUpgrade": { + "type": "string", + "allowedValues": [ + "Deferred", + "NotDeferred" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades)." + } + }, + "evictionPolicy": { + "type": "string", + "allowedValues": [ + "AllKeysLFU", + "AllKeysLRU", + "AllKeysRandom", + "NoEviction", + "VolatileLFU", + "VolatileLRU", + "VolatileRandom", + "VolatileTTL" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the eviction policy for the Redis resource." + } + }, + "geoReplication": { + "$ref": "#/definitions/geoReplicationType", + "nullable": true, + "metadata": { + "description": "Optional. The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration." + } + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/definitions/moduleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules)." + } + }, + "port": { + "type": "int", + "nullable": true, + "minValue": 10000, + "maxValue": 10000, + "metadata": { + "description": "Optional. TCP port of the database endpoint." + } + }, + "persistence": { + "$ref": "#/definitions/persistenceType", + "nullable": true, + "metadata": { + "description": "Optional. The persistence settings of the service." + } + }, + "accessPolicyAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/accessPolicyAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The database-level diagnostic settings of the service." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "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." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_2.aofPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "aof" + ], + "metadata": { + "description": "Required. AOF persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "1s" + ], + "metadata": { + "description": "Required. The frequency at which data is written to disk." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "_2.disabledPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "disabled" + ], + "metadata": { + "description": "Required. Disabled persistence type." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "_2.linkedDatabaseType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "_2.rdbPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "rdb" + ], + "metadata": { + "description": "Required. RDB persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "12h", + "1h", + "6h" + ], + "metadata": { + "description": "Required. The frequency at which an RDB snapshot of the database is created." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "accessPolicyAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "accessPolicyName": { + "type": "string", + "allowedValues": [ + "default" + ], + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy to be assigned. The current only allowed name is 'default'." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "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, the deployment will use the latest version available at deployment time." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingLogsOnlyType": { + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingMetricsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric 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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "geoReplicationType": { + "type": "object", + "properties": { + "groupNickname": { + "type": "string", + "metadata": { + "description": "Required. The name of the geo-replication group." + } + }, + "linkedDatabases": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.linkedDatabaseType" + }, + "metadata": { + "description": "Required. List of database resources to link with this database, including itself." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityOnlyUserAssignedType": { + "type": "object", + "properties": { + "userAssignedResourceIds": { + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if only user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "moduleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "RediSearch", + "RedisBloom", + "RedisJSON", + "RedisTimeSeries" + ], + "metadata": { + "description": "Required. The name of the module." + } + }, + "args": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Additional module arguments." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "persistenceType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "disabled": { + "$ref": "#/definitions/_2.disabledPersistenceType" + }, + "aof": { + "$ref": "#/definitions/_2.aofPersistenceType" + }, + "rdb": { + "$ref": "#/definitions/_2.rdbPersistenceType" + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "privateEndpointSingleServiceType": { + "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." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "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": { + "$ref": "#/definitions/lockType", + "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 assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "primaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAccessKey secret name to create." + } + }, + "primaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryConnectionString secret name to create." + } + }, + "primaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryStackExchangeRedisConnectionString secret name to create." + } + }, + "secondaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAccessKey secret name to create." + } + }, + "secondaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryConnectionString secret name to create." + } + }, + "secondaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryStackExchangeRedisConnectionString secret name to create." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the cache resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityOnlyUserAssignedType", + "nullable": true, + "metadata": { + "description": "Conditional. The managed identity definition for this resource. Required if 'customerManagedKey' is not empty." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition to use for the managed service." + } + }, + "highAvailability": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Specifies whether to enable data replication for high availability. Used only with Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. HIGH AVAILABILITY IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-high-availability) FOR CLARIFICATION." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "1.2", + "allowedValues": [ + "1.2" + ], + "metadata": { + "description": "Optional. The minimum TLS version for the Redis cluster to support." + } + }, + "capacity": { + "type": "int", + "defaultValue": 2, + "allowedValues": [ + 2, + 3, + 4, + 6, + 8, + 9, + 10 + ], + "metadata": { + "description": "Optional. The size of the cluster. Only supported on Redis Enterprise SKUs: Enterprise, EnterpriseFlash. Valid values are (2, 4, 6, 8, 10) for Enterprise SKUs and (3, 9) for EnterpriseFlash SKUs. [Learn more](https://learn.microsoft.com/azure/azure-cache-for-redis/cache-best-practices-enterprise-tiers#sharding-and-cpu-utilization)." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Enterprise_E5", + "allowedValues": [ + "Balanced_B0", + "Balanced_B1", + "Balanced_B3", + "Balanced_B5", + "Balanced_B10", + "Balanced_B20", + "Balanced_B50", + "Balanced_B100", + "Balanced_B150", + "Balanced_B250", + "Balanced_B350", + "Balanced_B500", + "Balanced_B700", + "Balanced_B1000", + "ComputeOptimized_X3", + "ComputeOptimized_X5", + "ComputeOptimized_X10", + "ComputeOptimized_X20", + "ComputeOptimized_X50", + "ComputeOptimized_X100", + "ComputeOptimized_X150", + "ComputeOptimized_X250", + "ComputeOptimized_X350", + "ComputeOptimized_X500", + "ComputeOptimized_X700", + "Enterprise_E1", + "Enterprise_E5", + "Enterprise_E10", + "Enterprise_E20", + "Enterprise_E50", + "Enterprise_E100", + "Enterprise_E200", + "Enterprise_E400", + "EnterpriseFlash_F300", + "EnterpriseFlash_F700", + "EnterpriseFlash_F1500", + "FlashOptimized_A250", + "FlashOptimized_A500", + "FlashOptimized_A700", + "FlashOptimized_A1000", + "FlashOptimized_A1500", + "FlashOptimized_A2000", + "FlashOptimized_A4500", + "MemoryOptimized_M10", + "MemoryOptimized_M20", + "MemoryOptimized_M50", + "MemoryOptimized_M100", + "MemoryOptimized_M150", + "MemoryOptimized_M250", + "MemoryOptimized_M350", + "MemoryOptimized_M500", + "MemoryOptimized_M700", + "MemoryOptimized_M1000", + "MemoryOptimized_M1500", + "MemoryOptimized_M2000" + ], + "metadata": { + "description": "Optional. The type of cluster to deploy. Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized ARE IN PREVIEW, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview#tiers-and-skus-at-a-glance) FOR CLARIFICATION." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. The Availability Zones to place the resources in. Currently only supported on Enterprise and EnterpriseFlash SKUs." + } + }, + "database": { + "$ref": "#/definitions/databaseType", + "nullable": true, + "metadata": { + "description": "Optional. Database configuration." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingMetricsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The cluster-level diagnostic settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "isAmr": "[or(or(or(startsWith(parameters('skuName'), 'Balanced'), startsWith(parameters('skuName'), 'ComputeOptimized')), startsWith(parameters('skuName'), 'FlashOptimized')), startsWith(parameters('skuName'), 'MemoryOptimized'))]", + "isEnterprise": "[or(startsWith(parameters('skuName'), 'Enterprise'), startsWith(parameters('skuName'), 'EnterpriseFlash'))]", + "availabilityZones": "[if(variables('isEnterprise'), map(parameters('zones'), lambda('zone', string(lambdaVariables('zone')))), createArray())]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), 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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "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')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Role Based Access Control Administrator": "[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(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cache-redisenterprise.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "redisCluster": { + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('customerManagedKeyEncryption', createObject('keyEncryptionKeyIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('identityType', 'userAssignedIdentity', 'userAssignedIdentityResourceId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2], split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')))), null()), 'keyEncryptionKeyUrl', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "highAvailability": "[if(variables('isAmr'), parameters('highAvailability'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]" + }, + "sku": { + "capacity": "[if(variables('isEnterprise'), parameters('capacity'), null())]", + "name": "[parameters('skuName')]" + }, + "zones": "[if(not(empty(variables('availabilityZones'))), variables('availabilityZones'), null())]", + "dependsOn": [ + "cMKKeyVault::cMKKey" + ] + }, + "redisCluster_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Cache/redisEnterprise/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "redisCluster" + ] + }, + "redisCluster_diagnosticSettings": { + "copy": { + "name": "redisCluster_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Cache/redisEnterprise/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "redisCluster" + ] + }, + "redisCluster_roleAssignments": { + "copy": { + "name": "redisCluster_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Cache/redisEnterprise/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "redisCluster" + ] + }, + "redisCluster_database": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-redis-database', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('database'), 'name')]" + }, + "redisClusterName": { + "value": "[parameters('name')]" + }, + "accessKeysAuthentication": "[if(variables('isAmr'), createObject('value', tryGet(parameters('database'), 'accessKeysAuthentication')), createObject('value', null()))]", + "accessPolicyAssignments": "[if(variables('isAmr'), createObject('value', tryGet(parameters('database'), 'accessPolicyAssignments')), createObject('value', null()))]", + "clientProtocol": { + "value": "[tryGet(parameters('database'), 'clientProtocol')]" + }, + "clusteringPolicy": { + "value": "[tryGet(parameters('database'), 'clusteringPolicy')]" + }, + "deferUpgrade": { + "value": "[tryGet(parameters('database'), 'deferUpgrade')]" + }, + "evictionPolicy": { + "value": "[tryGet(parameters('database'), 'evictionPolicy')]" + }, + "geoReplication": { + "value": "[tryGet(parameters('database'), 'geoReplication')]" + }, + "modules": { + "value": "[tryGet(parameters('database'), 'modules')]" + }, + "port": { + "value": "[tryGet(parameters('database'), 'port')]" + }, + "persistence": { + "value": "[tryGet(parameters('database'), 'persistence')]" + }, + "secretsExportConfiguration": { + "value": "[tryGet(parameters('database'), 'secretsExportConfiguration')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('database'), 'diagnosticSettings')]" + } + }, + "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.33.93.31351", + "templateHash": "12094637252334328193" + }, + "name": "Redis database", + "description": "This module deploys a Redis database in a Redis Enterprise or Azure Managed Redis (Preview) cluster." + }, + "definitions": { + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "primaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAccessKey secret name to create." + } + }, + "primaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryConnectionString secret name to create." + } + }, + "primaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryStackExchangeRedisConnectionString secret name to create." + } + }, + "secondaryAccessKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAccessKey secret name to create." + } + }, + "secondaryConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryConnectionString secret name to create." + } + }, + "secondaryStackExchangeRedisConnectionStringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryStackExchangeRedisConnectionString secret name to create." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "disabledPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "disabled" + ], + "metadata": { + "description": "Required. Disabled persistence type." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "aofPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "aof" + ], + "metadata": { + "description": "Required. AOF persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "1s" + ], + "metadata": { + "description": "Required. The frequency at which data is written to disk." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "rdbPersistenceType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "rdb" + ], + "metadata": { + "description": "Required. RDB persistence type." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "12h", + "1h", + "6h" + ], + "metadata": { + "description": "Required. The frequency at which an RDB snapshot of the database is created." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "persistenceType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "disabled": { + "$ref": "#/definitions/disabledPersistenceType" + }, + "aof": { + "$ref": "#/definitions/aofPersistenceType" + }, + "rdb": { + "$ref": "#/definitions/rdbPersistenceType" + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "moduleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "RediSearch", + "RedisBloom", + "RedisJSON", + "RedisTimeSeries" + ], + "metadata": { + "description": "Required. The name of the module." + } + }, + "args": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Additional module arguments." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "geoReplicationType": { + "type": "object", + "properties": { + "groupNickname": { + "type": "string", + "metadata": { + "description": "Required. The name of the geo-replication group." + } + }, + "linkedDatabases": { + "type": "array", + "items": { + "$ref": "#/definitions/linkedDatabaseType" + }, + "metadata": { + "description": "Required. List of database resources to link with this database, including itself." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "linkedDatabaseType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of linked database. Should be in the form: `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Cache/redisEnterprise/{redisName}/databases/default`." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "accessPolicyAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "accessPolicyName": { + "type": "string", + "allowedValues": [ + "default" + ], + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy to be assigned. The current only allowed name is 'default'." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.secretSetOutputType": { + "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." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingLogsOnlyType": { + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "redisClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Redis Enterprise or Azure Managed Redis (Preview) resource. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. Name of the database." + } + }, + "accessKeysAuthentication": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Allow authentication via access keys. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#disable-access-key-authentication-on-your-cache) FOR CLARIFICATION." + } + }, + "clientProtocol": { + "type": "string", + "defaultValue": "Encrypted", + "allowedValues": [ + "Encrypted", + "Plaintext" + ], + "metadata": { + "description": "Optional. Specifies whether Redis clients can connect using TLS-encrypted or plaintext Redis protocols." + } + }, + "clusteringPolicy": { + "type": "string", + "defaultValue": "OSSCluster", + "allowedValues": [ + "EnterpriseCluster", + "OSSCluster" + ], + "metadata": { + "description": "Optional. Redis clustering policy. [Learn more](https://aka.ms/redis/enterprise/clustering)." + } + }, + "deferUpgrade": { + "type": "string", + "defaultValue": "NotDeferred", + "allowedValues": [ + "Deferred", + "NotDeferred" + ], + "metadata": { + "description": "Optional. Specifies whether to defer future Redis major version upgrades by up to 90 days. [Learn more](https://aka.ms/redisversionupgrade#defer-upgrades)." + } + }, + "evictionPolicy": { + "type": "string", + "defaultValue": "VolatileLRU", + "allowedValues": [ + "AllKeysLFU", + "AllKeysLRU", + "AllKeysRandom", + "NoEviction", + "VolatileLFU", + "VolatileLRU", + "VolatileRandom", + "VolatileTTL" + ], + "metadata": { + "description": "Optional. Specifies the eviction policy for the Redis resource." + } + }, + "geoReplication": { + "$ref": "#/definitions/geoReplicationType", + "nullable": true, + "metadata": { + "description": "Optional. The active geo-replication settings of the service. All caches within a geo-replication group must have the same configuration." + } + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/definitions/moduleType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Redis modules to enable. Restrictions may apply based on SKU and configuration. [Learn more](https://aka.ms/redis/enterprise/modules)." + } + }, + "port": { + "type": "int", + "defaultValue": 10000, + "minValue": 10000, + "maxValue": 10000, + "metadata": { + "description": "Optional. TCP port of the database endpoint." + } + }, + "persistence": { + "$ref": "#/definitions/persistenceType", + "defaultValue": { + "type": "disabled" + }, + "metadata": { + "description": "Optional. The persistence settings of the service." + } + }, + "accessPolicyAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/accessPolicyAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Access policy assignments for Microsoft Entra authentication. Only supported on Azure Managed Redis (Preview) SKUs: Balanced, ComputeOptimized, FlashOptimized, and MemoryOptimized. THIS IS A PARAMETER USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) FOR CLARIFICATION." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The database-level diagnostic settings of the service." + } + } + }, + "resources": { + "redisCluster": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('redisClusterName')]" + }, + "redisDatabase": { + "type": "Microsoft.Cache/redisEnterprise/databases", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}', parameters('redisClusterName'), parameters('name'))]", + "properties": { + "accessKeysAuthentication": "[if(or(or(or(startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'Balanced'), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'ComputeOptimized')), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'FlashOptimized')), startsWith(reference('redisCluster', '2024-09-01-preview', 'full').sku.name, 'MemoryOptimized')), parameters('accessKeysAuthentication'), null())]", + "clientProtocol": "[parameters('clientProtocol')]", + "clusteringPolicy": "[parameters('clusteringPolicy')]", + "deferUpgrade": "[parameters('deferUpgrade')]", + "evictionPolicy": "[parameters('evictionPolicy')]", + "geoReplication": "[if(not(empty(parameters('geoReplication'))), parameters('geoReplication'), null())]", + "modules": "[parameters('modules')]", + "port": "[parameters('port')]", + "persistence": "[if(not(equals(parameters('persistence').type, 'disabled')), createObject('aofEnabled', equals(parameters('persistence').type, 'aof'), 'rdbEnabled', equals(parameters('persistence').type, 'rdb'), 'aofFrequency', if(equals(parameters('persistence').type, 'aof'), parameters('persistence').frequency, null()), 'rdbFrequency', if(equals(parameters('persistence').type, 'rdb'), parameters('persistence').frequency, null())), null())]" + }, + "dependsOn": [ + "redisCluster" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Cache/redisEnterprise/{0}/databases/{1}', parameters('redisClusterName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "redisDatabase" + ] + }, + "database_accessPolicyAssignments": { + "copy": { + "name": "database_accessPolicyAssignments", + "count": "[length(coalesce(parameters('accessPolicyAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-redis-apa-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()].name]" + }, + "clusterName": { + "value": "[parameters('redisClusterName')]" + }, + "databaseName": { + "value": "[parameters('name')]" + }, + "accessPolicyName": { + "value": "[tryGet(coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()], 'accessPolicyName')]" + }, + "userObjectId": { + "value": "[coalesce(parameters('accessPolicyAssignments'), createArray())[copyIndex()].userObjectId]" + } + }, + "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.33.93.31351", + "templateHash": "6473026260124919331" + }, + "name": "Azure Managed Redis (Preview) Database Access Policy Assignment", + "description": "This module deploys an access policy assignment for an Azure Managed Redis (Preview) database." + }, + "parameters": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the access policy assignment." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Required. Object ID to which the access policy will be assigned." + } + }, + "clusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the grandparent Azure Managed Redis (Preview) cluster. Required if the template is used in a standalone deployment." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Managed Redis (Preview) database. Required if the template is used in a standalone deployment." + } + }, + "accessPolicyName": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default" + ], + "metadata": { + "description": "Optional. Name of the access policy to be assigned." + } + } + }, + "resources": { + "redisCluster::database": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise/databases", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('databaseName'))]" + }, + "redisCluster": { + "existing": true, + "type": "Microsoft.Cache/redisEnterprise", + "apiVersion": "2024-09-01-preview", + "name": "[parameters('clusterName')]" + }, + "accessPolicyAssignment": { + "type": "Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments", + "apiVersion": "2024-09-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]", + "properties": { + "accessPolicyName": "[parameters('accessPolicyName')]", + "user": { + "objectId": "[parameters('userObjectId')]" + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policy assignment." + }, + "value": "[coalesce(parameters('name'), parameters('userObjectId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policy assignment." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments', parameters('clusterName'), parameters('databaseName'), coalesce(parameters('name'), parameters('userObjectId')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the access policy assignment was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "The object ID of the user associated with the access policy." + }, + "value": "[reference('accessPolicyAssignment').user.objectId]" + } + } + } + }, + "dependsOn": [ + "redisDatabase" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAccessKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryAccessKeyName'), 'value', listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryConnectionStringName'), 'value', format('{0}:{1}@{2}:{3}', if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'redis://', 'rediss://'), listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey, reference('redisCluster').hostName, reference('redisDatabase').port))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryStackExchangeRedisConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryStackExchangeRedisConnectionStringName'), 'value', format('{0}:{1},password={2},ssl={3},abortConnect=False', reference('redisCluster').hostName, reference('redisDatabase').port, listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').primaryKey, if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'False', 'True')))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAccessKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryAccessKeyName'), 'value', listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryConnectionStringName'), 'value', format('{0}:{1}@{2}:{3}', if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'redis://', 'rediss://'), listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey, reference('redisCluster').hostName, reference('redisDatabase').port))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryStackExchangeRedisConnectionStringName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryStackExchangeRedisConnectionStringName'), 'value', format('{0}:{1},password={2},ssl={3},abortConnect=False', reference('redisCluster').hostName, reference('redisDatabase').port, listKeys(resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name')), '2024-09-01-preview').secondaryKey, if(equals(reference('redisDatabase').clientProtocol, 'Plaintext'), 'False', 'True')))), 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.33.93.31351", + "templateHash": "8063348652715653257" + } + }, + "definitions": { + "secretSetOutputType": { + "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." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the secrets 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]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "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]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "redisCluster", + "redisDatabase" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Redis database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the database." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('redisClusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Redis resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "port": { + "type": "int", + "metadata": { + "description": "The Redis database port." + }, + "value": "[reference('redisDatabase').port]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The Redis endpoint." + }, + "value": "[format('{0}:{1}', reference('redisCluster').hostName, reference('redisDatabase').port)]" + }, + "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())]" + } + } + } + }, + "dependsOn": [ + "redisCluster" + ] + }, + "redisEnterprise_privateEndpoints": { + "copy": { + "name": "redisEnterprise_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-redisEnterprise-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'redisEnterprise'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'redisEnterprise'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'redisEnterprise')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'redisEnterprise'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Cache/redisEnterprise', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'redisEnterprise')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[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.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "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." + } + }, + "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." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "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." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "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": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "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.33.13.18514", + "templateHash": "5440815542537978381" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. 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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "redisCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Redis cluster." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Redis cluster." + }, + "value": "[resourceId('Microsoft.Cache/redisEnterprise', parameters('name'))]" + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "The name of the Redis database." + }, + "value": "[reference('redisCluster_database').outputs.name.value]" + }, + "databaseResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the database." + }, + "value": "[reference('redisCluster_database').outputs.resourceId.value]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Redis resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The Redis endpoint." + }, + "value": "[reference('redisCluster_database').outputs.endpoint.value]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('redisCluster', '2024-09-01-preview', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the Redis resource." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('redisEnterprise_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('redisEnterprise_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('redisEnterprise_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('redisEnterprise_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('redisEnterprise_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + }, + "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": "[reference('redisCluster_database').outputs.exportedSecrets.value]" + } + } +} diff --git a/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/dependencies.bicep new file mode 100644 index 0000000000..b759cd2526 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/dependencies.bicep @@ -0,0 +1,42 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Redis Enterprise cluster.') +param redisClusterName string + +@description('Optional. The name of the geo-replication group.') +param geoReplicationGroupName string = 'geo-replication-group' + +resource redisCluster 'Microsoft.Cache/redisEnterprise@2024-09-01-preview' = { + name: redisClusterName + location: location + sku: { + name: 'Balanced_B10' + } + + resource redisDatabase 'databases@2024-09-01-preview' = { + name: 'default' + properties: { + geoReplication: { + groupNickname: geoReplicationGroupName + linkedDatabases: [ + { + id: '${redisCluster.id}/databases/default' + } + ] + } + } + } +} + +@description('The resource ID of the created Redis database') +output redisDbResourceId string = redisCluster::redisDatabase.id + +@description('The location of the created Redis Cache') +output redisLocation string = redisCluster.location + +@description('The name of the created Redis Cache') +output redisClusterName string = redisCluster.name + +@description('The name of the geo-replication group') +output geoReplicationGroupName string = redisCluster::redisDatabase.properties.geoReplication.groupNickname diff --git a/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/main.test.bicep new file mode 100644 index 0000000000..6ad2cbba2e --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/active-geo-replication/main.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +metadata name = 'Active geo-replication' +metadata description = 'This instance deploys the module with active geo-replication enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${serviceShort}-rg' + +@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 = 'creagr' + +@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_#' + +// Not all regions support zone-redundancy, so hardcoding 2 zone-enabled locations here +#disable-next-line no-hardcoded-location +var enforcedLocation = 'germanywestcentral' +#disable-next-line no-hardcoded-location +var enforcedPairedLocation = 'uksouth' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' + params: { + redisClusterName: '${namePrefix}${serviceShort}001' + location: enforcedLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedPairedLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}002' + location: enforcedPairedLocation + skuName: 'Balanced_B10' + database: { + geoReplication: { + groupNickname: nestedDependencies.outputs.geoReplicationGroupName + linkedDatabases: [ + { + id: nestedDependencies.outputs.redisDbResourceId + } + { + id: '${resourceGroup.id}/providers/Microsoft.Cache/redisEnterprise/${namePrefix}${serviceShort}002/databases/default' + } + ] + } + } + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/dependencies.bicep new file mode 100644 index 0000000000..d73a514a3c --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to be created.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/main.test.bicep new file mode 100644 index 0000000000..905af1f642 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/amr-entra-id/main.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'subscription' + +metadata name = 'Azure Managed Redis (Preview) with Entra ID authentication' +metadata description = 'This instance deploys an Azure Managed Redis (Preview) cache with Entra ID authentication.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${serviceShort}-rg' + +@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 = 'creaei' + +@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_#' + +// Hardcoded to 'uksouth' because AMR SKUs are not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' + params: { + location: enforcedLocation + managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: enforcedLocation + skuName: 'Balanced_B10' + database: { + accessKeysAuthentication: 'Disabled' + accessPolicyAssignments: [ + { + name: 'assign1' + userObjectId: nestedDependencies.outputs.managedIdentityPrincipalId + } + ] + } + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/amr/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/amr/main.test.bicep new file mode 100644 index 0000000000..83b4090b45 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/amr/main.test.bicep @@ -0,0 +1,49 @@ +targetScope = 'subscription' + +metadata name = 'Azure Managed Redis (Preview)' +metadata description = 'This instance deploys an Azure Managed Redis (Preview) cache with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${serviceShort}-rg' + +@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 = 'creamr' + +@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_#' + +// Hardcoded to 'uksouth' because AMR SKUs are not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + skuName: 'Balanced_B10' + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/defaults/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..a69a39227a --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,48 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${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 = 'cremin' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-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' + skuName: 'Balanced_B10' + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/dependencies.bicep new file mode 100644 index 0000000000..b93119b7bb --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/dependencies.bicep @@ -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 resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/main.test.bicep new file mode 100644 index 0000000000..553e9485eb --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/kv-secrets/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'Deploying with a key vault reference to save secrets' +metadata description = 'This instance deploys the module saving all its secrets 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}-cache-redisenterprise-${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 = 'crekvs' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============== // +// General resources +// ============== // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}kvref' + skuName: 'Balanced_B10' + database: { + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + primaryAccessKeyName: 'custom-primaryAccessKey-name' + primaryConnectionStringName: 'custom-primaryConnectionString-name' + primaryStackExchangeRedisConnectionStringName: 'custom-primaryStackExchangeRedisConnectionString-name' + secondaryAccessKeyName: 'custom-secondaryAccessKey-name' + secondaryConnectionStringName: 'custom-secondaryConnectionString-name' + secondaryStackExchangeRedisConnectionStringName: 'custom-secondaryStackExchangeRedisConnectionString-name' + } + } + } +} diff --git a/avm/res/cache/redis-enterprise/tests/e2e/max/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..c1b5396310 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/max/dependencies.bicep @@ -0,0 +1,63 @@ +@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 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.redis.azure.net' + 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 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 Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/cache/redis-enterprise/tests/e2e/max/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..6da9399595 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/max/main.test.bicep @@ -0,0 +1,175 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${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 = 'cremax' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-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: 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' + location: resourceLocation + skuName: 'Balanced_B10' + database: { + clientProtocol: 'Plaintext' + clusteringPolicy: 'EnterpriseCluster' + deferUpgrade: 'Deferred' + evictionPolicy: 'NoEviction' + modules: [ + { + name: 'RedisBloom' + } + { + name: 'RediSearch' + } + ] + persistence: { + type: 'aof' + frequency: '1s' + } + diagnosticSettings: [ + { + name: 'customSettingDatabase' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + enableTelemetry: true + diagnosticSettings: [ + { + name: 'customSettingCluster' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + highAvailability: 'Disabled' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + minimumTlsVersion: '1.2' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + name: '759769d2-fc52-4a92-a943-724e48927e0b' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + zones: [ + 1 + 2 + 3 + ] + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep new file mode 100644 index 0000000000..b12458a9b8 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -0,0 +1,63 @@ +@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 for encryption to work + 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/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/main.test.bicep new file mode 100644 index 0000000000..77614d60c7 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/user-assigned-cmk-encryption/main.test.bicep @@ -0,0 +1,75 @@ +targetScope = 'subscription' + +metadata name = 'Using Customer-Managed-Keys with User-Assigned identity' +metadata description = 'This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${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 = 'creuace' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-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)}' + 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' + skuName: 'Balanced_B10' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + database: { + persistence: { type: 'rdb', frequency: '6h' } + } + } + } +] diff --git a/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..f4a2b05950 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,49 @@ +@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 + +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.redis.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 privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep b/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..a02bf8b192 --- /dev/null +++ b/avm/res/cache/redis-enterprise/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,132 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cache-redisenterprise-${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 = 'crewaf' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-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: 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' + diagnosticSettings: [ + { + name: 'customSettingCluster' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + database: { + diagnosticSettings: [ + { + name: 'customSettingDatabase' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + persistence: { + type: 'rdb' + frequency: '1h' + } + } + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + skuName: 'Balanced_B10' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + zones: [ + 1 + 2 + 3 + ] + } + } +] diff --git a/avm/res/cache/redis-enterprise/version.json b/avm/res/cache/redis-enterprise/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/cache/redis-enterprise/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From a5a930f1ab0d3bae90b9df64d629d8ffe9a979d8 Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Thu, 20 Feb 2025 13:34:44 +0100 Subject: [PATCH 46/53] feat: First PR for new module `avm/res/hybrid-compute/gateway` (#4402) Initial pull request for new AVM Module `avm/res/hybrid-compute/gateway` * https://github.com/Azure/Azure-Verified-Modules/issues/1833 <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.hybrid-compute.gateway](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.gateway.yml/badge.svg?branch=avm-res-hybrid-compute-gateway)](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.gateway.yml) | <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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 - [ ] 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 - [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 date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --------- Co-authored-by: Alexander Sehr <ASehr@hotmail.de> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.hybrid-compute.gateway.yml | 88 ++++++ avm/res/hybrid-compute/gateway/README.md | 269 ++++++++++++++++++ avm/res/hybrid-compute/gateway/main.bicep | 88 ++++++ avm/res/hybrid-compute/gateway/main.json | 181 ++++++++++++ .../tests/e2e/defaults/main.test.bicep | 47 +++ .../tests/e2e/waf-aligned/main.test.bicep | 47 +++ avm/res/hybrid-compute/gateway/version.json | 7 + 9 files changed, 729 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.hybrid-compute.gateway.yml create mode 100644 avm/res/hybrid-compute/gateway/README.md create mode 100644 avm/res/hybrid-compute/gateway/main.bicep create mode 100644 avm/res/hybrid-compute/gateway/main.json create mode 100644 avm/res/hybrid-compute/gateway/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/hybrid-compute/gateway/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/hybrid-compute/gateway/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 706ed99129..cc47a747a0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -92,7 +92,7 @@ /avm/res/fabric/capacity/ @Azure/avm-res-fabric-capacity-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/health-bot/health-bot/ @Azure/avm-res-healthbot-healthbot-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/healthcare-apis/workspace/ @Azure/avm-res-healthcareapis-workspace-module-owners-bicep @Azure/avm-module-reviewers-bicep -/avm/res/hybrid-compute/license/ @Azure/avm-res-hybridcompute-license-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/hybrid-compute/gateway/ @Azure/avm-res-hybridcompute-gateway-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/hybrid-compute/machine/ @Azure/avm-res-hybridcompute-machine-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/action-group/ @Azure/avm-res-insights-actiongroup-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/activity-log-alert/ @Azure/avm-res-insights-activitylogalert-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index f6162bc1c6..6e4d56c906 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -127,7 +127,7 @@ body: - "avm/res/fabric/capacity" - "avm/res/health-bot/health-bot" - "avm/res/healthcare-apis/workspace" - - "avm/res/hybrid-compute/license" + - "avm/res/hybrid-compute/gateway" - "avm/res/hybrid-compute/machine" - "avm/res/insights/action-group" - "avm/res/insights/activity-log-alert" diff --git a/.github/workflows/avm.res.hybrid-compute.gateway.yml b/.github/workflows/avm.res.hybrid-compute.gateway.yml new file mode 100644 index 0000000000..ec2a09da55 --- /dev/null +++ b/.github/workflows/avm.res.hybrid-compute.gateway.yml @@ -0,0 +1,88 @@ +name: "avm.res.hybrid-compute.gateway" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.hybrid-compute.gateway.yml" + - "avm/res/hybrid-compute/gateway/**" + - "utilities/pipelines/**" + - "!utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/hybrid-compute/gateway" + workflowPath: ".github/workflows/avm.res.hybrid-compute.gateway.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/hybrid-compute/gateway/README.md b/avm/res/hybrid-compute/gateway/README.md new file mode 100644 index 0000000000..4eeb18ef3e --- /dev/null +++ b/avm/res/hybrid-compute/gateway/README.md @@ -0,0 +1,269 @@ +# Azure Arc Gateway `[Microsoft.HybridCompute/gateways]` + +This module deploys a Azure Arc Gateway. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## 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.HybridCompute/gateways` | [2024-07-31-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-07-31-preview/gateways) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/hybrid-compute/gateway:<version>`. + +- [Azure Arc Gateway using only the defaults](#example-1-azure-arc-gateway-using-only-the-defaults) +- [Azure Arc Gateway according to WAF](#example-2-azure-arc-gateway-according-to-waf) + +### Example 1: _Azure Arc Gateway using only the defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module gateway 'br/public:avm/res/hybrid-compute/gateway:<version>' = { + name: 'gatewayDeployment' + params: { + name: 'hcgmin001' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "hcgmin001" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/hybrid-compute/gateway:<version>' + +param name = 'hcgmin001' +``` + +</details> +<p> + +### Example 2: _Azure Arc Gateway according to WAF_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module gateway 'br/public:avm/res/hybrid-compute/gateway:<version>' = { + name: 'gatewayDeployment' + params: { + name: 'hcgwaf001' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "hcgwaf001" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/hybrid-compute/gateway:<version>' + +param name = 'hcgwaf001' +``` + +</details> +<p> + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the resource to create. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedFeatures`](#parameter-allowedfeatures) | array | Specifies the list of features that are enabled for this Gateway. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`gatewayType`](#parameter-gatewaytype) | string | The type of the Gateway resource. | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `name` + +Name of the resource to create. + +- Required: Yes +- Type: string + +### Parameter: `allowedFeatures` + +Specifies the list of features that are enabled for this Gateway. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + '*' + ] + ``` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `gatewayType` + +The type of the Gateway resource. + +- Required: No +- Type: string +- Default: `'Public'` +- Allowed: + ```Bicep + [ + 'Public' + ] + ``` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the resource. | +| `resourceGroupName` | string | The resource group of the deployed storage account. | +| `resourceId` | string | The resource ID of the resource. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/hybrid-compute/gateway/main.bicep b/avm/res/hybrid-compute/gateway/main.bicep new file mode 100644 index 0000000000..5498e1c0c8 --- /dev/null +++ b/avm/res/hybrid-compute/gateway/main.bicep @@ -0,0 +1,88 @@ +metadata name = 'Azure Arc Gateway' +metadata description = 'This module deploys a Azure Arc Gateway.' + +@description('Required. Name of the resource to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Specifies the list of features that are enabled for this Gateway.') +param allowedFeatures string[] = ['*'] + +@description('Optional. The type of the Gateway resource.') +@allowed([ + 'Public' +]) +param gatewayType string = 'Public' + +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The lock settings of the service.') +param lock lockType? + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.hybridcompute-gateway.${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 gateway 'Microsoft.HybridCompute/gateways@2024-07-31-preview' = { + name: name + location: location + tags: tags + properties: { + allowedFeatures: allowedFeatures + gatewayType: gatewayType + } +} + +resource gateway_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: gateway +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the resource.') +output resourceId string = gateway.id + +@description('The name of the resource.') +output name string = gateway.name + +@description('The location the resource was deployed into.') +output location string = gateway.location + +@description('The resource group of the deployed storage account.') +output resourceGroupName string = resourceGroup().name + diff --git a/avm/res/hybrid-compute/gateway/main.json b/avm/res/hybrid-compute/gateway/main.json new file mode 100644 index 0000000000..6923ba1911 --- /dev/null +++ b/avm/res/hybrid-compute/gateway/main.json @@ -0,0 +1,181 @@ +{ + "$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.33.93.31351", + "templateHash": "10888168310844370299" + }, + "name": "Azure Arc Gateway", + "description": "This module deploys a Azure Arc Gateway." + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the resource to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFeatures": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [ + "*" + ], + "metadata": { + "description": "Optional. Specifies the list of features that are enabled for this Gateway." + } + }, + "gatewayType": { + "type": "string", + "defaultValue": "Public", + "allowedValues": [ + "Public" + ], + "metadata": { + "description": "Optional. The type of the Gateway resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.hybridcompute-gateway.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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" + } + } + } + } + }, + "gateway": { + "type": "Microsoft.HybridCompute/gateways", + "apiVersion": "2024-07-31-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "allowedFeatures": "[parameters('allowedFeatures')]", + "gatewayType": "[parameters('gatewayType')]" + } + }, + "gateway_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.HybridCompute/gateways/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "gateway" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the resource." + }, + "value": "[resourceId('Microsoft.HybridCompute/gateways', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the resource." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('gateway', '2024-07-31-preview', 'full').location]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/hybrid-compute/gateway/tests/e2e/defaults/main.test.bicep b/avm/res/hybrid-compute/gateway/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..2ed6e91359 --- /dev/null +++ b/avm/res/hybrid-compute/gateway/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,47 @@ +targetScope = 'subscription' + +metadata name = 'Azure Arc Gateway using only the defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.gateway-${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 = 'hcgmin' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-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' + } + } +] diff --git a/avm/res/hybrid-compute/gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/hybrid-compute/gateway/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..129285646a --- /dev/null +++ b/avm/res/hybrid-compute/gateway/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,47 @@ +targetScope = 'subscription' + +metadata name = 'Azure Arc Gateway according to WAF' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-hybridCompute.gateway-${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 = 'hcgwaf' + +@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 // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-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' + } + } +] diff --git a/avm/res/hybrid-compute/gateway/version.json b/avm/res/hybrid-compute/gateway/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/hybrid-compute/gateway/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 9ebc9c0bdb34d25f4a0328f0cf24b07bf9f83105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= <rene@hezser.de> Date: Thu, 20 Feb 2025 21:39:01 +0100 Subject: [PATCH 47/53] feat: Update `avm/res/api-management/service` - WAF Security recommendations (#3942) ## Description Adds WAF Security defaults and UDTs. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.api-management.service](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml/badge.svg?branch=api-gateway-waf-security)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] 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. - [x] 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`. - [x] 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 --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Alexander Sehr <ASehr@hotmail.de> --- avm/res/api-management/service/README.md | 33 ++++++-- .../service/api-version-set/main.json | 4 +- .../service/api/diagnostics/main.json | 4 +- avm/res/api-management/service/api/main.json | 12 +-- .../service/api/policy/main.json | 4 +- avm/res/api-management/service/main.bicep | 4 +- avm/res/api-management/service/main.json | 76 +++++++++---------- .../tests/e2e/defaults/main.test.bicep | 2 +- .../tests/e2e/developerSku/main.test.bicep | 2 +- .../service/tests/e2e/max/main.test.bicep | 10 +-- .../service/tests/e2e/v2Sku/main.test.bicep | 2 +- .../tests/e2e/waf-aligned/main.test.bicep | 10 +-- avm/res/api-management/service/version.json | 2 +- 13 files changed, 93 insertions(+), 72 deletions(-) diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index 290ab46d6c..03538fc39e 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -342,6 +342,10 @@ module service 'br/public:avm/res/api-management/service:<version>' = { displayName: 'Echo API' name: 'echo-api' path: 'echo' + protocols: [ + 'http' + 'https' + ] serviceUrl: 'http://echoapi.cloudapp.net/api' } ] @@ -578,6 +582,10 @@ module service 'br/public:avm/res/api-management/service:<version>' = { "displayName": "Echo API", "name": "echo-api", "path": "echo", + "protocols": [ + "http", + "https" + ], "serviceUrl": "http://echoapi.cloudapp.net/api" } ] @@ -840,6 +848,10 @@ param apis = [ displayName: 'Echo API' name: 'echo-api' path: 'echo' + protocols: [ + 'http' + 'https' + ] serviceUrl: 'http://echoapi.cloudapp.net/api' } ] @@ -1140,6 +1152,9 @@ module service 'br/public:avm/res/api-management/service:<version>' = { displayName: 'Echo API' name: 'echo-api' path: 'echo' + protocols: [ + 'https' + ] serviceUrl: 'https://echoapi.cloudapp.net/api' } ] @@ -1163,8 +1178,8 @@ module service 'br/public:avm/res/api-management/service:<version>' = { { name: 'backend' tls: { - validateCertificateChain: false - validateCertificateName: false + validateCertificateChain: true + validateCertificateName: true } url: 'https://echoapi.cloudapp.net/api' } @@ -1351,6 +1366,9 @@ module service 'br/public:avm/res/api-management/service:<version>' = { "displayName": "Echo API", "name": "echo-api", "path": "echo", + "protocols": [ + "https" + ], "serviceUrl": "https://echoapi.cloudapp.net/api" } ] @@ -1378,8 +1396,8 @@ module service 'br/public:avm/res/api-management/service:<version>' = { { "name": "backend", "tls": { - "validateCertificateChain": false, - "validateCertificateName": false + "validateCertificateChain": true, + "validateCertificateName": true }, "url": "https://echoapi.cloudapp.net/api" } @@ -1584,6 +1602,9 @@ param apis = [ displayName: 'Echo API' name: 'echo-api' path: 'echo' + protocols: [ + 'https' + ] serviceUrl: 'https://echoapi.cloudapp.net/api' } ] @@ -1607,8 +1628,8 @@ param backends = [ { name: 'backend' tls: { - validateCertificateChain: false - validateCertificateName: false + validateCertificateChain: true + validateCertificateName: true } url: 'https://echoapi.cloudapp.net/api' } diff --git a/avm/res/api-management/service/api-version-set/main.json b/avm/res/api-management/service/api-version-set/main.json index b002431030..7680abc895 100644 --- a/avm/res/api-management/service/api-version-set/main.json +++ b/avm/res/api-management/service/api-version-set/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "6801592949576181154" + "version": "0.33.93.31351", + "templateHash": "7829811049560910009" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set." diff --git a/avm/res/api-management/service/api/diagnostics/main.json b/avm/res/api-management/service/api/diagnostics/main.json index 790023763f..18c08257d6 100644 --- a/avm/res/api-management/service/api/diagnostics/main.json +++ b/avm/res/api-management/service/api/diagnostics/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4899675580903703157" + "version": "0.33.93.31351", + "templateHash": "13183080858238494781" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics." diff --git a/avm/res/api-management/service/api/main.json b/avm/res/api-management/service/api/main.json index de7b395213..f48b80272d 100644 --- a/avm/res/api-management/service/api/main.json +++ b/avm/res/api-management/service/api/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "11063536724422240669" + "version": "0.33.93.31351", + "templateHash": "273590710214674608" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API." @@ -279,8 +279,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8003925948340237461" + "version": "0.33.93.31351", + "templateHash": "1494563992508164069" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy." @@ -425,8 +425,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4899675580903703157" + "version": "0.33.93.31351", + "templateHash": "13183080858238494781" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics." diff --git a/avm/res/api-management/service/api/policy/main.json b/avm/res/api-management/service/api/policy/main.json index 87e44f9560..9783d01165 100644 --- a/avm/res/api-management/service/api/policy/main.json +++ b/avm/res/api-management/service/api/policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8003925948340237461" + "version": "0.33.93.31351", + "templateHash": "1494563992508164069" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy." diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index bacb6ca151..b5ff91656b 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -282,7 +282,7 @@ module service_apis 'api/main.bicep' = [ apiVersionDescription: api.?apiVersionDescription apiVersionSetId: api.?apiVersionSetId authenticationSettings: api.?authenticationSettings - format: api.?format ?? 'openapi' + format: api.?format isCurrent: api.?isCurrent protocols: api.?protocols policies: api.?policies @@ -351,7 +351,7 @@ module service_backends 'backend/main.bicep' = [ resourceId: backend.?resourceId serviceFabricCluster: backend.?serviceFabricCluster title: backend.?title - tls: backend.?tls + tls: backend.?tls ?? { validateCertificateChain: true, validateCertificateName: true } } } ] diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index bff37609e8..d8fc02aaef 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "14024053243776143462" + "version": "0.33.93.31351", + "templateHash": "16108971413887515803" }, "name": "API Management Services", "description": "This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU." @@ -782,7 +782,7 @@ "value": "[tryGet(parameters('apis')[copyIndex()], 'authenticationSettings')]" }, "format": { - "value": "[coalesce(tryGet(parameters('apis')[copyIndex()], 'format'), 'openapi')]" + "value": "[tryGet(parameters('apis')[copyIndex()], 'format')]" }, "isCurrent": { "value": "[tryGet(parameters('apis')[copyIndex()], 'isCurrent')]" @@ -822,8 +822,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "11063536724422240669" + "version": "0.33.93.31351", + "templateHash": "273590710214674608" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API." @@ -1096,8 +1096,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8003925948340237461" + "version": "0.33.93.31351", + "templateHash": "1494563992508164069" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy." @@ -1242,8 +1242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4899675580903703157" + "version": "0.33.93.31351", + "templateHash": "13183080858238494781" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics." @@ -1469,8 +1469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "6801592949576181154" + "version": "0.33.93.31351", + "templateHash": "7829811049560910009" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set." @@ -1609,8 +1609,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9439651007814693141" + "version": "0.33.93.31351", + "templateHash": "6804762094873651550" }, "name": "API Management Service Authorization Servers", "description": "This module deploys an API Management Service Authorization Server." @@ -1848,7 +1848,7 @@ "value": "[tryGet(parameters('backends')[copyIndex()], 'title')]" }, "tls": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'tls')]" + "value": "[coalesce(tryGet(parameters('backends')[copyIndex()], 'tls'), createObject('validateCertificateChain', true(), 'validateCertificateName', true()))]" } }, "template": { @@ -1858,8 +1858,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "13471923779604074887" + "version": "0.33.93.31351", + "templateHash": "4453336321720967633" }, "name": "API Management Service Backends", "description": "This module deploys an API Management Service Backend." @@ -2038,8 +2038,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "3359248846501864533" + "version": "0.33.93.31351", + "templateHash": "18419808380672694533" }, "name": "API Management Service Caches", "description": "This module deploys an API Management Service Cache." @@ -2192,8 +2192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "4899675580903703157" + "version": "0.33.93.31351", + "templateHash": "13183080858238494781" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics." @@ -2421,8 +2421,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "9439755619586446330" + "version": "0.33.93.31351", + "templateHash": "13263983509172438133" }, "name": "API Management Service Identity Providers", "description": "This module deploys an API Management Service Identity Provider." @@ -2630,8 +2630,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "13044725911661445483" + "version": "0.33.93.31351", + "templateHash": "9800847829037569395" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger." @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "8157254408403610295" + "version": "0.33.93.31351", + "templateHash": "11386329254828299336" }, "name": "API Management Service Named Values", "description": "This module deploys an API Management Service Named Value." @@ -2911,8 +2911,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "3588318966794177458" + "version": "0.33.93.31351", + "templateHash": "2236873279760395261" }, "name": "API Management Service Portal Settings", "description": "This module deploys an API Management Service Portal Setting." @@ -3009,8 +3009,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "12529707644768894354" + "version": "0.33.93.31351", + "templateHash": "1189152396458775709" }, "name": "API Management Service Policies", "description": "This module deploys an API Management Service Policy." @@ -3143,8 +3143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "7427869826758534329" + "version": "0.33.93.31351", + "templateHash": "9602469673358610075" }, "name": "API Management Service Products", "description": "This module deploys an API Management Service Product." @@ -3271,8 +3271,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "6881101310103461222" + "version": "0.33.93.31351", + "templateHash": "7187924573150749189" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API." @@ -3360,8 +3360,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "7173352386317054073" + "version": "0.33.93.31351", + "templateHash": "12351031153434834126" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group." @@ -3519,8 +3519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "3748720225613039754" + "version": "0.33.93.31351", + "templateHash": "16999168968925536172" }, "name": "API Management Service Subscriptions", "description": "This module deploys an API Management Service Subscription." diff --git a/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep b/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep index d1dd29a05c..54d81f75c1 100644 --- a/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep b/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep index d9f8a2d610..4aef7f9900 100644 --- a/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/api-management/service/tests/e2e/max/main.test.bicep b/avm/res/api-management/service/tests/e2e/max/main.test.bicep index e8d162bf6b..162cb6f991 100644 --- a/avm/res/api-management/service/tests/e2e/max/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/max/main.test.bicep @@ -33,7 +33,7 @@ param customSecret string = newGuid() // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } @@ -111,6 +111,10 @@ module testDeployment '../../../main.bicep' = [ name: 'echo-api' path: 'echo' serviceUrl: 'http://echoapi.cloudapp.net/api' + protocols: [ + 'http' + 'https' + ] } ] authorizationServers: { @@ -287,9 +291,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep b/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep index bb37ce6721..742a6ad819 100644 --- a/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-11-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep index 6bef96d931..2453e2a05a 100644 --- a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep @@ -115,6 +115,9 @@ module testDeployment '../../../main.bicep' = [ name: 'echo-api' path: 'echo' serviceUrl: 'https://echoapi.cloudapp.net/api' + protocols: [ + 'https' + ] } ] authorizationServers: { @@ -137,8 +140,8 @@ module testDeployment '../../../main.bicep' = [ { name: 'backend' tls: { - validateCertificateChain: false - validateCertificateName: false + validateCertificateChain: true + validateCertificateName: true } url: 'https://echoapi.cloudapp.net/api' } @@ -253,8 +256,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - diagnosticDependencies - ] } ] diff --git a/avm/res/api-management/service/version.json b/avm/res/api-management/service/version.json index b6668657e7..b39a201436 100644 --- a/avm/res/api-management/service/version.json +++ b/avm/res/api-management/service/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] From ba82449e788bf058ef165a381577664d322ef00e Mon Sep 17 00:00:00 2001 From: Peter Budai <peterbudai@hotmail.com> Date: Fri, 21 Feb 2025 21:25:37 +0100 Subject: [PATCH 48/53] feat: new module: `avm/res/network/network-security-perimeter` (#4278) ## Description Adding new module `avm/res/network/network-security-perimeter` See https://github.com/Azure/Azure-Verified-Modules/issues/1709 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.network.network-security-perimeter](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.res.network.network-security-perimeter.yml/badge.svg?branch=nsp)](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.res.network.network-security-perimeter.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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 Pls. note that network security perimeter should be specifically enabled in the subscription: https://learn.microsoft.com/en-us/azure/private-link/create-network-security-perimeter-portal#prerequisites <!-- Please keep up to date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...res.network.network-security-perimeter.yml | 88 ++ .../network-security-perimeter/README.md | 1069 +++++++++++++++++ .../network-security-perimeter/main.bicep | 211 ++++ .../network-security-perimeter/main.json | 1019 ++++++++++++++++ .../profile/README.md | 159 +++ .../profile/access-rule/README.md | 140 +++ .../profile/access-rule/main.bicep | 66 + .../profile/access-rule/main.json | 163 +++ .../profile/main.bicep | 79 ++ .../profile/main.json | 380 ++++++ .../tests/e2e/defaults/main.test.bicep | 48 + .../tests/e2e/max/dependencies.bicep | 36 + .../tests/e2e/max/main.test.bicep | 138 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 25 + .../tests/e2e/waf-aligned/main.test.bicep | 111 ++ .../network-security-perimeter/version.json | 7 + 18 files changed, 3741 insertions(+) create mode 100644 .github/workflows/avm.res.network.network-security-perimeter.yml create mode 100644 avm/res/network/network-security-perimeter/README.md create mode 100644 avm/res/network/network-security-perimeter/main.bicep create mode 100644 avm/res/network/network-security-perimeter/main.json create mode 100644 avm/res/network/network-security-perimeter/profile/README.md create mode 100644 avm/res/network/network-security-perimeter/profile/access-rule/README.md create mode 100644 avm/res/network/network-security-perimeter/profile/access-rule/main.bicep create mode 100644 avm/res/network/network-security-perimeter/profile/access-rule/main.json create mode 100644 avm/res/network/network-security-perimeter/profile/main.bicep create mode 100644 avm/res/network/network-security-perimeter/profile/main.json create mode 100644 avm/res/network/network-security-perimeter/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/network/network-security-perimeter/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/network/network-security-perimeter/tests/e2e/max/main.test.bicep create mode 100644 avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/network/network-security-perimeter/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cc47a747a0..783616851d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -139,6 +139,7 @@ /avm/res/network/network-interface/ @Azure/avm-res-network-networkinterface-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/network/network-manager/ @Azure/avm-res-network-networkmanager-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/network/network-security-group/ @Azure/avm-res-network-networksecuritygroup-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/network/network-security-perimeter/ @Azure/avm-res-network-networksecurityperimeter-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/network/network-watcher/ @Azure/avm-res-network-networkwatcher-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/network/p2s-vpn-gateway/ @Azure/avm-res-network-p2svpngateway-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/network/private-dns-zone/ @Azure/avm-res-network-privatednszone-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 6e4d56c906..ec2688d5d9 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -174,6 +174,7 @@ body: - "avm/res/network/network-interface" - "avm/res/network/network-manager" - "avm/res/network/network-security-group" + - "avm/res/network/network-security-perimeter" - "avm/res/network/network-watcher" - "avm/res/network/p2s-vpn-gateway" - "avm/res/network/private-dns-zone" diff --git a/.github/workflows/avm.res.network.network-security-perimeter.yml b/.github/workflows/avm.res.network.network-security-perimeter.yml new file mode 100644 index 0000000000..7f70cfdea8 --- /dev/null +++ b/.github/workflows/avm.res.network.network-security-perimeter.yml @@ -0,0 +1,88 @@ +name: "avm.res.network.network-security-perimeter" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.network.network-security-perimeter.yml" + - "avm/res/network/network-security-perimeter/**" + - "utilities/pipelines/**" + - "!utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/network/network-security-perimeter" + workflowPath: ".github/workflows/avm.res.network.network-security-perimeter.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/network/network-security-perimeter/README.md b/avm/res/network/network-security-perimeter/README.md new file mode 100644 index 0000000000..ddb33c6cd8 --- /dev/null +++ b/avm/res/network/network-security-perimeter/README.md @@ -0,0 +1,1069 @@ +# Network Security Perimeter `[Microsoft.Network/networkSecurityPerimeters]` + +This module deploys a Network Security Perimeter (NSP). + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## 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.Network/networkSecurityPerimeters` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters) | +| `Microsoft.Network/networkSecurityPerimeters/profiles` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/profiles) | +| `Microsoft.Network/networkSecurityPerimeters/profiles/accessRules` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/profiles/accessRules) | +| `Microsoft.Network/networkSecurityPerimeters/resourceAssociations` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/resourceAssociations) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/network-security-perimeter:<version>`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module networkSecurityPerimeter 'br/public:avm/res/network/network-security-perimeter:<version>' = { + name: 'networkSecurityPerimeterDeployment' + params: { + // Required parameters + name: 'nnspmin001' + // Non-required parameters + location: '<location>' + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nnspmin001" + }, + // Non-required parameters + "location": { + "value": "<location>" + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/network/network-security-perimeter:<version>' + +// Required parameters +param name = 'nnspmin001' +// Non-required parameters +param location = '<location>' +``` + +</details> +<p> + +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module networkSecurityPerimeter 'br/public:avm/res/network/network-security-perimeter:<version>' = { + name: 'networkSecurityPerimeterDeployment' + params: { + // Required parameters + name: 'nnspmax001' + // Non-required parameters + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + name: 'customSetting' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + location: '<location>' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + profiles: [ + { + accessRules: [ + { + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + name: 'rule-outbound-01' + } + { + addressPrefixes: [ + '198.168.1.0/24' + ] + direction: 'Inbound' + name: 'rule-inbound-01' + } + ] + name: 'profile-01' + } + ] + resourceAssociations: [ + { + accessMode: 'Learning' + privateLinkResource: '<privateLinkResource>' + profile: 'profile-01' + } + ] + roleAssignments: [ + { + name: 'b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '<name>' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '<roleDefinitionIdOrName>' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nnspmax001" + }, + // Non-required parameters + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "name": "customSetting", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ] + }, + "location": { + "value": "<location>" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "profiles": { + "value": [ + { + "accessRules": [ + { + "direction": "Outbound", + "fullyQualifiedDomainNames": [ + "www.contoso.com" + ], + "name": "rule-outbound-01" + }, + { + "addressPrefixes": [ + "198.168.1.0/24" + ], + "direction": "Inbound", + "name": "rule-inbound-01" + } + ], + "name": "profile-01" + } + ] + }, + "resourceAssociations": { + "value": [ + { + "accessMode": "Learning", + "privateLinkResource": "<privateLinkResource>", + "profile": "profile-01" + } + ] + }, + "roleAssignments": { + "value": [ + { + "name": "b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8", + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "<name>", + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "<principalId>", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "<roleDefinitionIdOrName>" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/network/network-security-perimeter:<version>' + +// Required parameters +param name = 'nnspmax001' +// Non-required parameters +param diagnosticSettings = [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + name: 'customSetting' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } +] +param location = '<location>' +param lock = { + kind: 'CanNotDelete' + name: 'myCustomLockName' +} +param profiles = [ + { + accessRules: [ + { + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + name: 'rule-outbound-01' + } + { + addressPrefixes: [ + '198.168.1.0/24' + ] + direction: 'Inbound' + name: 'rule-inbound-01' + } + ] + name: 'profile-01' + } +] +param resourceAssociations = [ + { + accessMode: 'Learning' + privateLinkResource: '<privateLinkResource>' + profile: 'profile-01' + } +] +param roleAssignments = [ + { + name: 'b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '<name>' + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '<principalId>' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '<roleDefinitionIdOrName>' + } +] +param tags = { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' +} +``` + +</details> +<p> + +### Example 3: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module networkSecurityPerimeter 'br/public:avm/res/network/network-security-perimeter:<version>' = { + name: 'networkSecurityPerimeterDeployment' + params: { + // Required parameters + name: 'nnspwaf001' + // Non-required parameters + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + name: 'customSetting' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } + ] + location: '<location>' + profiles: [ + { + accessRules: [ + { + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + name: 'rule-outbound-01' + } + { + addressPrefixes: [ + '198.168.1.0/24' + ] + direction: 'Inbound' + name: 'rule-inbound-01' + } + ] + name: 'profile-01' + } + ] + resourceAssociations: [ + { + accessMode: 'Learning' + privateLinkResource: '<privateLinkResource>' + profile: 'profile-01' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON parameters file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nnspwaf001" + }, + // Non-required parameters + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "<eventHubAuthorizationRuleResourceId>", + "eventHubName": "<eventHubName>", + "name": "customSetting", + "storageAccountResourceId": "<storageAccountResourceId>", + "workspaceResourceId": "<workspaceResourceId>" + } + ] + }, + "location": { + "value": "<location>" + }, + "profiles": { + "value": [ + { + "accessRules": [ + { + "direction": "Outbound", + "fullyQualifiedDomainNames": [ + "www.contoso.com" + ], + "name": "rule-outbound-01" + }, + { + "addressPrefixes": [ + "198.168.1.0/24" + ], + "direction": "Inbound", + "name": "rule-inbound-01" + } + ], + "name": "profile-01" + } + ] + }, + "resourceAssociations": { + "value": [ + { + "accessMode": "Learning", + "privateLinkResource": "<privateLinkResource>", + "profile": "profile-01" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +</details> +<p> + +<details> + +<summary>via Bicep parameters file</summary> + +```bicep-params +using 'br/public:avm/res/network/network-security-perimeter:<version>' + +// Required parameters +param name = 'nnspwaf001' +// Non-required parameters +param diagnosticSettings = [ + { + eventHubAuthorizationRuleResourceId: '<eventHubAuthorizationRuleResourceId>' + eventHubName: '<eventHubName>' + name: 'customSetting' + storageAccountResourceId: '<storageAccountResourceId>' + workspaceResourceId: '<workspaceResourceId>' + } +] +param location = '<location>' +param profiles = [ + { + accessRules: [ + { + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + name: 'rule-outbound-01' + } + { + addressPrefixes: [ + '198.168.1.0/24' + ] + direction: 'Inbound' + name: 'rule-inbound-01' + } + ] + name: 'profile-01' + } +] +param resourceAssociations = [ + { + accessMode: 'Learning' + privateLinkResource: '<privateLinkResource>' + profile: 'profile-01' + } +] +param tags = { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' +} +``` + +</details> +<p> + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Network Security Perimeter. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`profiles`](#parameter-profiles) | array | Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed. | +| [`resourceAssociations`](#parameter-resourceassociations) | array | Array of resource associations to create. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags of the NSG resource. | + +### Parameter: `name` + +Name of the Network Security Perimeter. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +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. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `profiles` + +Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-profilesname) | string | The name of the network security perimeter profile. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessRules`](#parameter-profilesaccessrules) | array | Whether network traffic is allowed or denied. | + +### Parameter: `profiles.name` + +The name of the network security perimeter profile. + +- Required: Yes +- Type: string + +### Parameter: `profiles.accessRules` + +Whether network traffic is allowed or denied. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`direction`](#parameter-profilesaccessrulesdirection) | string | The type for an access rule. | +| [`name`](#parameter-profilesaccessrulesname) | string | The name of the access rule. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefixes`](#parameter-profilesaccessrulesaddressprefixes) | array | Inbound address prefixes (IPv4/IPv6).s. | +| [`emailAddresses`](#parameter-profilesaccessrulesemailaddresses) | array | Outbound rules email address format. | +| [`fullyQualifiedDomainNames`](#parameter-profilesaccessrulesfullyqualifieddomainnames) | array | Outbound rules fully qualified domain name format. | +| [`phoneNumbers`](#parameter-profilesaccessrulesphonenumbers) | array | Outbound rules phone number format. | +| [`serviceTags`](#parameter-profilesaccessrulesservicetags) | array | Inbound rules service tag names. | +| [`subscriptions`](#parameter-profilesaccessrulessubscriptions) | array | List of subscription ids. | + +### Parameter: `profiles.accessRules.direction` + +The type for an access rule. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Inbound' + 'Outbound' + ] + ``` + +### Parameter: `profiles.accessRules.name` + +The name of the access rule. + +- Required: Yes +- Type: string + +### Parameter: `profiles.accessRules.addressPrefixes` + +Inbound address prefixes (IPv4/IPv6).s. + +- Required: No +- Type: array + +### Parameter: `profiles.accessRules.emailAddresses` + +Outbound rules email address format. + +- Required: No +- Type: array + +### Parameter: `profiles.accessRules.fullyQualifiedDomainNames` + +Outbound rules fully qualified domain name format. + +- Required: No +- Type: array + +### Parameter: `profiles.accessRules.phoneNumbers` + +Outbound rules phone number format. + +- Required: No +- Type: array + +### Parameter: `profiles.accessRules.serviceTags` + +Inbound rules service tag names. + +- Required: No +- Type: array + +### Parameter: `profiles.accessRules.subscriptions` + +List of subscription ids. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-profilesaccessrulessubscriptionsid) | string | The subscription id. | + +### Parameter: `profiles.accessRules.subscriptions.id` + +The subscription id. + +- Required: Yes +- Type: string + +### Parameter: `resourceAssociations` + +Array of resource associations to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateLinkResource`](#parameter-resourceassociationsprivatelinkresource) | string | The resource identifier of the resource association. | +| [`profile`](#parameter-resourceassociationsprofile) | string | The name of the resource association. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessMode`](#parameter-resourceassociationsaccessmode) | string | The access mode of the resource association. | + +### Parameter: `resourceAssociations.privateLinkResource` + +The resource identifier of the resource association. + +- Required: Yes +- Type: string + +### Parameter: `resourceAssociations.profile` + +The name of the resource association. + +- Required: Yes +- Type: string + +### Parameter: `resourceAssociations.accessMode` + +The access mode of the resource association. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Audit' + 'Enforced' + 'Learning' + ] + ``` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Tags of the NSG resource. + +- Required: No +- Type: object + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the network security perimeter. | +| `resourceGroupName` | string | The resource group the network security perimeter was deployed into. | +| `resourceId` | string | The resource ID of the network security perimeter. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other 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 | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/network-security-perimeter/main.bicep b/avm/res/network/network-security-perimeter/main.bicep new file mode 100644 index 0000000000..7f2b18d22b --- /dev/null +++ b/avm/res/network/network-security-perimeter/main.bicep @@ -0,0 +1,211 @@ +metadata name = 'Network Security Perimeter' +metadata description = 'This module deploys a Network Security Perimeter (NSP).' + +@description('Required. Name of the Network Security Perimeter.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed.') +param profiles profileType[]? + +@description('Optional. Array of resource associations to create.') +param resourceAssociations resourceAssociationType[]? + +import { diagnosticSettingLogsOnlyType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingLogsOnlyType[]? + +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. The lock settings of the service.') +param lock lockType? + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +@description('Optional. Tags of the NSG resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Network Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4d97b98b-1d4f-4787-a291-c67834d212e7' + ) + 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': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.network-nwsecurityperimeter.${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 networkSecurityPerimeter 'Microsoft.Network/networkSecurityPerimeters@2023-08-01-preview' = { + name: name + location: location + tags: tags +} + +module networkSecurityPerimeter_profiles 'profile/main.bicep' = [ + for (profile, index) in (profiles ?? []): { + name: '${uniqueString(deployment().name, location)}-nsp-profile-${index}' + params: { + networkPerimeterName: name + name: profile.name + accessRules: profile.?accessRules + } + } +] + +resource networkSecurityPerimeter_resourceAssociations 'Microsoft.Network/networkSecurityPerimeters/resourceAssociations@2023-08-01-preview' = [ + for (resourceAssociation, index) in (resourceAssociations ?? []): { + name: '${guid(resourceAssociation.privateLinkResource, name, resourceAssociation.profile)}-nsp-ra' + parent: networkSecurityPerimeter + properties: { + privateLinkResource: { + id: resourceAssociation.privateLinkResource + } + profile: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/networkSecurityPerimeters/profiles', name, resourceAssociation.profile)}' + } + accessMode: resourceAssociation.accessMode ?? 'Learning' + } + dependsOn: [ + networkSecurityPerimeter_profiles + ] + } +] + +resource networkSecurityPerimeter_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + scope: networkSecurityPerimeter + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } +} + +resource networkSecurityPerimeter_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + scope: networkSecurityPerimeter + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + } +] + +resource networkSecurityPerimeter_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + networkSecurityPerimeter.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) + scope: networkSecurityPerimeter + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + } +] + +@description('The resource group the network security perimeter was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the network security perimeter.') +output resourceId string = networkSecurityPerimeter.id + +@description('The name of the network security perimeter.') +output name string = networkSecurityPerimeter.name + +@description('The location the resource was deployed into.') +output location string = networkSecurityPerimeter.location + +// =============== // +// Definitions // +// =============== // + +import { accessRuleType } from 'profile/main.bicep' + +@export() +@description('The type for a profile.') +type profileType = { + @description('Required. The name of the network security perimeter profile.') + name: string + + @description('Optional. Whether network traffic is allowed or denied.') + accessRules: accessRuleType[]? +} + +@export() +@description('The type for a resource association.') +type resourceAssociationType = { + @description('Required. The resource identifier of the resource association.') + privateLinkResource: string + + @description('Required. The name of the resource association.') + profile: string + + @description('Optional. The access mode of the resource association.') + accessMode: 'Learning' | 'Audit' | 'Enforced'? +} diff --git a/avm/res/network/network-security-perimeter/main.json b/avm/res/network/network-security-perimeter/main.json new file mode 100644 index 0000000000..954b092c43 --- /dev/null +++ b/avm/res/network/network-security-perimeter/main.json @@ -0,0 +1,1019 @@ +{ + "$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.33.93.31351", + "templateHash": "5001761265450278752" + }, + "name": "Network Security Perimeter", + "description": "This module deploys a Network Security Perimeter (NSP)." + }, + "definitions": { + "profileType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network security perimeter profile." + } + }, + "accessRules": { + "type": "array", + "items": { + "$ref": "#/definitions/accessRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a profile." + } + }, + "resourceAssociationType": { + "type": "object", + "properties": { + "privateLinkResource": { + "type": "string", + "metadata": { + "description": "Required. The resource identifier of the resource association." + } + }, + "profile": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource association." + } + }, + "accessMode": { + "type": "string", + "allowedValues": [ + "Audit", + "Enforced", + "Learning" + ], + "nullable": true, + "metadata": { + "description": "Optional. The access mode of the resource association." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a resource association." + } + }, + "accessRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "metadata": { + "description": "The type for an access rule.", + "__bicep_imported_from!": { + "sourceTemplate": "profile/main.bicep" + } + } + }, + "diagnosticSettingLogsOnlyType": { + "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." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "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." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Perimeter." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "profiles": { + "type": "array", + "items": { + "$ref": "#/definitions/profileType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "resourceAssociations": { + "type": "array", + "items": { + "$ref": "#/definitions/resourceAssociationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of resource associations to create." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "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": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-nwsecurityperimeter.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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" + } + } + } + } + }, + "networkSecurityPerimeter": { + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "networkSecurityPerimeter_resourceAssociations": { + "copy": { + "name": "networkSecurityPerimeter_resourceAssociations", + "count": "[length(coalesce(parameters('resourceAssociations'), createArray()))]" + }, + "type": "Microsoft.Network/networkSecurityPerimeters/resourceAssociations", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('name'), format('{0}-nsp-ra', guid(coalesce(parameters('resourceAssociations'), createArray())[copyIndex()].privateLinkResource, parameters('name'), coalesce(parameters('resourceAssociations'), createArray())[copyIndex()].profile)))]", + "properties": { + "privateLinkResource": { + "id": "[coalesce(parameters('resourceAssociations'), createArray())[copyIndex()].privateLinkResource]" + }, + "profile": { + "id": "[format('{0}', resourceId('Microsoft.Network/networkSecurityPerimeters/profiles', parameters('name'), coalesce(parameters('resourceAssociations'), createArray())[copyIndex()].profile))]" + }, + "accessMode": "[coalesce(coalesce(parameters('resourceAssociations'), createArray())[copyIndex()].accessMode, 'Learning')]" + }, + "dependsOn": [ + "networkSecurityPerimeter", + "networkSecurityPerimeter_profiles" + ] + }, + "networkSecurityPerimeter_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkSecurityPerimeters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkSecurityPerimeter" + ] + }, + "networkSecurityPerimeter_diagnosticSettings": { + "copy": { + "name": "networkSecurityPerimeter_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityPerimeters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityPerimeter" + ] + }, + "networkSecurityPerimeter_roleAssignments": { + "copy": { + "name": "networkSecurityPerimeter_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityPerimeters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityPerimeters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityPerimeter" + ] + }, + "networkSecurityPerimeter_profiles": { + "copy": { + "name": "networkSecurityPerimeter_profiles", + "count": "[length(coalesce(parameters('profiles'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-nsp-profile-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "networkPerimeterName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('profiles'), createArray())[copyIndex()].name]" + }, + "accessRules": { + "value": "[tryGet(coalesce(parameters('profiles'), createArray())[copyIndex()], 'accessRules')]" + } + }, + "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.33.93.31351", + "templateHash": "12754329895696619344" + }, + "name": "Network Security Perimeter Profile", + "description": "This module deploys a Network Security Perimeter Profile." + }, + "definitions": { + "accessRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an access rule." + } + } + }, + "parameters": { + "networkPerimeterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. The name of the network security perimeter profile." + } + }, + "accessRules": { + "type": "array", + "items": { + "$ref": "#/definitions/accessRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Static Members to create for the network group. Contains virtual networks to add to the network group." + } + } + }, + "resources": { + "networkSecurityPerimeter": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('networkPerimeterName')]" + }, + "networkSecurityPerimeter_profile": { + "type": "Microsoft.Network/networkSecurityPerimeters/profiles", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('networkPerimeterName'), parameters('name'))]" + }, + "nsp_accessRules": { + "copy": { + "name": "nsp_accessRules", + "count": "[length(coalesce(parameters('accessRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-nsp-accessrule-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "networkPerimeterName": { + "value": "[parameters('networkPerimeterName')]" + }, + "networkPerimeterProfileName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('accessRules'), createArray())[copyIndex()].name]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "direction": { + "value": "[coalesce(parameters('accessRules'), createArray())[copyIndex()].direction]" + }, + "emailAddresses": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'emailAddresses')]" + }, + "fullyQualifiedDomainNames": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'fullyQualifiedDomainNames')]" + }, + "phoneNumbers": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'phoneNumbers')]" + }, + "serviceTags": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'serviceTags')]" + }, + "subscriptions": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'subscriptions')]" + } + }, + "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.33.93.31351", + "templateHash": "12994955439990027696" + }, + "name": "Network Security Perimeter Access Rule", + "description": "This module deploys a Network Security Perimeter Access Rule." + }, + "parameters": { + "networkPerimeterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment." + } + }, + "networkPerimeterProfileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "resources": { + "networkPerimeter::networkSecurityPerimeterProfile": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters/profiles", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'))]" + }, + "networkPerimeter": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('networkPerimeterName')]" + }, + "nsp_accessRule": { + "type": "Microsoft.Network/networkSecurityPerimeters/profiles/accessRules", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]", + "properties": { + "addressPrefixes": "[parameters('addressPrefixes')]", + "direction": "[parameters('direction')]", + "emailAddresses": "[parameters('emailAddresses')]", + "fullyQualifiedDomainNames": "[parameters('fullyQualifiedDomainNames')]", + "phoneNumbers": "[parameters('phoneNumbers')]", + "serviceTags": "[parameters('serviceTags')]", + "subscriptions": "[parameters('subscriptions')]" + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed profile." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters/profiles/accessRules', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed profile." + }, + "value": "[parameters('name')]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed profile." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters/profiles', parameters('networkPerimeterName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed profile." + }, + "value": "[parameters('name')]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security perimeter." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security perimeter." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityPerimeter', '2023-08-01-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-security-perimeter/profile/README.md b/avm/res/network/network-security-perimeter/profile/README.md new file mode 100644 index 0000000000..51809bf301 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/README.md @@ -0,0 +1,159 @@ +# Network Security Perimeter Profile `[Microsoft.Network/networkSecurityPerimeters/profiles]` + +This module deploys a Network Security Perimeter Profile. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkSecurityPerimeters/profiles` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/profiles) | +| `Microsoft.Network/networkSecurityPerimeters/profiles/accessRules` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/profiles/accessRules) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the network security perimeter profile. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`networkPerimeterName`](#parameter-networkperimetername) | string | The name of the parent network perimeter. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessRules`](#parameter-accessrules) | array | Static Members to create for the network group. Contains virtual networks to add to the network group. | + +### Parameter: `name` + +The name of the network security perimeter profile. + +- Required: Yes +- Type: string + +### Parameter: `networkPerimeterName` + +The name of the parent network perimeter. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `accessRules` + +Static Members to create for the network group. Contains virtual networks to add to the network group. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`direction`](#parameter-accessrulesdirection) | string | The type for an access rule. | +| [`name`](#parameter-accessrulesname) | string | The name of the access rule. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefixes`](#parameter-accessrulesaddressprefixes) | array | Inbound address prefixes (IPv4/IPv6).s. | +| [`emailAddresses`](#parameter-accessrulesemailaddresses) | array | Outbound rules email address format. | +| [`fullyQualifiedDomainNames`](#parameter-accessrulesfullyqualifieddomainnames) | array | Outbound rules fully qualified domain name format. | +| [`phoneNumbers`](#parameter-accessrulesphonenumbers) | array | Outbound rules phone number format. | +| [`serviceTags`](#parameter-accessrulesservicetags) | array | Inbound rules service tag names. | +| [`subscriptions`](#parameter-accessrulessubscriptions) | array | List of subscription ids. | + +### Parameter: `accessRules.direction` + +The type for an access rule. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Inbound' + 'Outbound' + ] + ``` + +### Parameter: `accessRules.name` + +The name of the access rule. + +- Required: Yes +- Type: string + +### Parameter: `accessRules.addressPrefixes` + +Inbound address prefixes (IPv4/IPv6).s. + +- Required: No +- Type: array + +### Parameter: `accessRules.emailAddresses` + +Outbound rules email address format. + +- Required: No +- Type: array + +### Parameter: `accessRules.fullyQualifiedDomainNames` + +Outbound rules fully qualified domain name format. + +- Required: No +- Type: array + +### Parameter: `accessRules.phoneNumbers` + +Outbound rules phone number format. + +- Required: No +- Type: array + +### Parameter: `accessRules.serviceTags` + +Inbound rules service tag names. + +- Required: No +- Type: array + +### Parameter: `accessRules.subscriptions` + +List of subscription ids. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-accessrulessubscriptionsid) | string | The subscription id. | + +### Parameter: `accessRules.subscriptions.id` + +The subscription id. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed profile. | +| `resourceGroupName` | string | The resource group the network security perimeter was deployed into. | +| `resourceId` | string | The resource ID of the deployed profile. | diff --git a/avm/res/network/network-security-perimeter/profile/access-rule/README.md b/avm/res/network/network-security-perimeter/profile/access-rule/README.md new file mode 100644 index 0000000000..e82af484b0 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/access-rule/README.md @@ -0,0 +1,140 @@ +# Network Security Perimeter Access Rule `[Microsoft.Network/networkSecurityPerimeters/profiles/accessRules]` + +This module deploys a Network Security Perimeter Access Rule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkSecurityPerimeters/profiles/accessRules` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-08-01-preview/networkSecurityPerimeters/profiles/accessRules) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`direction`](#parameter-direction) | string | The type for an access rule. | +| [`name`](#parameter-name) | string | The name of the access rule. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`networkPerimeterName`](#parameter-networkperimetername) | string | The name of the parent network perimeter. Required if the template is used in a standalone deployment. | +| [`networkPerimeterProfileName`](#parameter-networkperimeterprofilename) | string | The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefixes`](#parameter-addressprefixes) | array | Inbound address prefixes (IPv4/IPv6).s. | +| [`emailAddresses`](#parameter-emailaddresses) | array | Outbound rules email address format. | +| [`fullyQualifiedDomainNames`](#parameter-fullyqualifieddomainnames) | array | Outbound rules fully qualified domain name format. | +| [`phoneNumbers`](#parameter-phonenumbers) | array | Outbound rules phone number format. | +| [`serviceTags`](#parameter-servicetags) | array | Inbound rules service tag names. | +| [`subscriptions`](#parameter-subscriptions) | array | List of subscription ids. | + +### Parameter: `direction` + +The type for an access rule. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Inbound' + 'Outbound' + ] + ``` + +### Parameter: `name` + +The name of the access rule. + +- Required: Yes +- Type: string + +### Parameter: `networkPerimeterName` + +The name of the parent network perimeter. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `networkPerimeterProfileName` + +The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `addressPrefixes` + +Inbound address prefixes (IPv4/IPv6).s. + +- Required: No +- Type: array + +### Parameter: `emailAddresses` + +Outbound rules email address format. + +- Required: No +- Type: array + +### Parameter: `fullyQualifiedDomainNames` + +Outbound rules fully qualified domain name format. + +- Required: No +- Type: array + +### Parameter: `phoneNumbers` + +Outbound rules phone number format. + +- Required: No +- Type: array + +### Parameter: `serviceTags` + +Inbound rules service tag names. + +- Required: No +- Type: array + +### Parameter: `subscriptions` + +List of subscription ids. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-subscriptionsid) | string | The subscription id. | + +### Parameter: `subscriptions.id` + +The subscription id. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed profile. | +| `resourceGroupName` | string | The resource group the network security perimeter was deployed into. | +| `resourceId` | string | The resource ID of the deployed profile. | diff --git a/avm/res/network/network-security-perimeter/profile/access-rule/main.bicep b/avm/res/network/network-security-perimeter/profile/access-rule/main.bicep new file mode 100644 index 0000000000..dd10231468 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/access-rule/main.bicep @@ -0,0 +1,66 @@ +metadata name = 'Network Security Perimeter Access Rule' +metadata description = 'This module deploys a Network Security Perimeter Access Rule.' + +@description('Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment.') +param networkPerimeterName string + +@description('Conditional. The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment.') +param networkPerimeterProfileName string + +@description('Required. The name of the access rule.') +param name string + +@description('Optional. Inbound address prefixes (IPv4/IPv6).s.') +param addressPrefixes string[]? + +@description('Required. The type for an access rule.') +param direction 'Inbound' | 'Outbound' + +@description('Optional. Outbound rules email address format.') +param emailAddresses string[]? + +@description('Optional. Outbound rules fully qualified domain name format.') +param fullyQualifiedDomainNames string[]? + +@description('Optional. Outbound rules phone number format.') +param phoneNumbers string[]? + +@description('Optional. Inbound rules service tag names.') +param serviceTags string[]? + +@description('Optional. List of subscription ids.') +param subscriptions { + @description('Required. The subscription id.') + id: string +}[]? + +resource networkPerimeter 'Microsoft.Network/networkSecurityPerimeters@2023-08-01-preview' existing = { + name: networkPerimeterName + + resource networkSecurityPerimeterProfile 'profiles@2023-08-01-preview' existing = { + name: networkPerimeterProfileName + } +} + +resource nsp_accessRule 'Microsoft.Network/networkSecurityPerimeters/profiles/accessRules@2023-08-01-preview' = { + name: name + parent: networkPerimeter::networkSecurityPerimeterProfile + properties: { + addressPrefixes: addressPrefixes + direction: direction + emailAddresses: emailAddresses + fullyQualifiedDomainNames: fullyQualifiedDomainNames + phoneNumbers: phoneNumbers + serviceTags: serviceTags + subscriptions: subscriptions + } +} + +@description('The resource group the network security perimeter was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the deployed profile.') +output resourceId string = nsp_accessRule.id + +@description('The name of the deployed profile.') +output name string = nsp_accessRule.name diff --git a/avm/res/network/network-security-perimeter/profile/access-rule/main.json b/avm/res/network/network-security-perimeter/profile/access-rule/main.json new file mode 100644 index 0000000000..10bbde4d25 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/access-rule/main.json @@ -0,0 +1,163 @@ +{ + "$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.33.93.31351", + "templateHash": "12994955439990027696" + }, + "name": "Network Security Perimeter Access Rule", + "description": "This module deploys a Network Security Perimeter Access Rule." + }, + "parameters": { + "networkPerimeterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment." + } + }, + "networkPerimeterProfileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "resources": { + "networkPerimeter::networkSecurityPerimeterProfile": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters/profiles", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'))]" + }, + "networkPerimeter": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('networkPerimeterName')]" + }, + "nsp_accessRule": { + "type": "Microsoft.Network/networkSecurityPerimeters/profiles/accessRules", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]", + "properties": { + "addressPrefixes": "[parameters('addressPrefixes')]", + "direction": "[parameters('direction')]", + "emailAddresses": "[parameters('emailAddresses')]", + "fullyQualifiedDomainNames": "[parameters('fullyQualifiedDomainNames')]", + "phoneNumbers": "[parameters('phoneNumbers')]", + "serviceTags": "[parameters('serviceTags')]", + "subscriptions": "[parameters('subscriptions')]" + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed profile." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters/profiles/accessRules', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed profile." + }, + "value": "[parameters('name')]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-security-perimeter/profile/main.bicep b/avm/res/network/network-security-perimeter/profile/main.bicep new file mode 100644 index 0000000000..bb358d67c5 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/main.bicep @@ -0,0 +1,79 @@ +metadata name = 'Network Security Perimeter Profile' +metadata description = 'This module deploys a Network Security Perimeter Profile.' + +@description('Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment.') +param networkPerimeterName string + +@maxLength(64) +@description('Required. The name of the network security perimeter profile.') +param name string + +@description('Optional. Static Members to create for the network group. Contains virtual networks to add to the network group.') +param accessRules accessRuleType[]? + +resource networkSecurityPerimeter 'Microsoft.Network/networkSecurityPerimeters@2023-08-01-preview' existing = { + name: networkPerimeterName +} + +resource networkSecurityPerimeter_profile 'Microsoft.Network/networkSecurityPerimeters/profiles@2023-08-01-preview' = { + name: name + parent: networkSecurityPerimeter +} + +module nsp_accessRules 'access-rule/main.bicep' = [ + for (accessRule, index) in (accessRules ?? []): { + name: '${uniqueString(deployment().name)}-nsp-accessrule-${index}' + params: { + networkPerimeterName: networkPerimeterName + networkPerimeterProfileName: name + name: accessRule.name + addressPrefixes: accessRule.?addressPrefixes + direction: accessRule.direction + emailAddresses: accessRule.?emailAddresses + fullyQualifiedDomainNames: accessRule.?fullyQualifiedDomainNames + phoneNumbers: accessRule.?phoneNumbers + serviceTags: accessRule.?serviceTags + subscriptions: accessRule.?subscriptions + } + } +] + +@description('The resource group the network security perimeter was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the deployed profile.') +output resourceId string = networkSecurityPerimeter_profile.id + +@description('The name of the deployed profile.') +output name string = networkSecurityPerimeter_profile.name + +@export() +@description('The type for an access rule.') +type accessRuleType = { + @description('Required. The name of the access rule.') + name: string + + @description('Required. The type for an access rule.') + direction: 'Inbound' | 'Outbound' + + @description('Optional. Inbound address prefixes (IPv4/IPv6).s.') + addressPrefixes: string[]? + + @description('Optional. Outbound rules email address format.') + emailAddresses: string[]? + + @description('Optional. Outbound rules fully qualified domain name format.') + fullyQualifiedDomainNames: string[]? + + @description('Optional. Outbound rules phone number format.') + phoneNumbers: string[]? + + @description('Optional. Inbound rules service tag names.') + serviceTags: string[]? + + @description('Optional. List of subscription ids.') + subscriptions: { + @description('Required. The subscription id.') + id: string + }[]? +} diff --git a/avm/res/network/network-security-perimeter/profile/main.json b/avm/res/network/network-security-perimeter/profile/main.json new file mode 100644 index 0000000000..79f56cbbd6 --- /dev/null +++ b/avm/res/network/network-security-perimeter/profile/main.json @@ -0,0 +1,380 @@ +{ + "$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.33.93.31351", + "templateHash": "12754329895696619344" + }, + "name": "Network Security Perimeter Profile", + "description": "This module deploys a Network Security Perimeter Profile." + }, + "definitions": { + "accessRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an access rule." + } + } + }, + "parameters": { + "networkPerimeterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. The name of the network security perimeter profile." + } + }, + "accessRules": { + "type": "array", + "items": { + "$ref": "#/definitions/accessRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Static Members to create for the network group. Contains virtual networks to add to the network group." + } + } + }, + "resources": { + "networkSecurityPerimeter": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('networkPerimeterName')]" + }, + "networkSecurityPerimeter_profile": { + "type": "Microsoft.Network/networkSecurityPerimeters/profiles", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('networkPerimeterName'), parameters('name'))]" + }, + "nsp_accessRules": { + "copy": { + "name": "nsp_accessRules", + "count": "[length(coalesce(parameters('accessRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-nsp-accessrule-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "networkPerimeterName": { + "value": "[parameters('networkPerimeterName')]" + }, + "networkPerimeterProfileName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('accessRules'), createArray())[copyIndex()].name]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "direction": { + "value": "[coalesce(parameters('accessRules'), createArray())[copyIndex()].direction]" + }, + "emailAddresses": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'emailAddresses')]" + }, + "fullyQualifiedDomainNames": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'fullyQualifiedDomainNames')]" + }, + "phoneNumbers": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'phoneNumbers')]" + }, + "serviceTags": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'serviceTags')]" + }, + "subscriptions": { + "value": "[tryGet(coalesce(parameters('accessRules'), createArray())[copyIndex()], 'subscriptions')]" + } + }, + "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.33.93.31351", + "templateHash": "12994955439990027696" + }, + "name": "Network Security Perimeter Access Rule", + "description": "This module deploys a Network Security Perimeter Access Rule." + }, + "parameters": { + "networkPerimeterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter. Required if the template is used in a standalone deployment." + } + }, + "networkPerimeterProfileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network perimeter Profile. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the access rule." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound address prefixes (IPv4/IPv6).s." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The type for an access rule." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules email address format." + } + }, + "fullyQualifiedDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules fully qualified domain name format." + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules phone number format." + } + }, + "serviceTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Inbound rules service tag names." + } + }, + "subscriptions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The subscription id." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subscription ids." + } + } + }, + "resources": { + "networkPerimeter::networkSecurityPerimeterProfile": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters/profiles", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'))]" + }, + "networkPerimeter": { + "existing": true, + "type": "Microsoft.Network/networkSecurityPerimeters", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('networkPerimeterName')]" + }, + "nsp_accessRule": { + "type": "Microsoft.Network/networkSecurityPerimeters/profiles/accessRules", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]", + "properties": { + "addressPrefixes": "[parameters('addressPrefixes')]", + "direction": "[parameters('direction')]", + "emailAddresses": "[parameters('emailAddresses')]", + "fullyQualifiedDomainNames": "[parameters('fullyQualifiedDomainNames')]", + "phoneNumbers": "[parameters('phoneNumbers')]", + "serviceTags": "[parameters('serviceTags')]", + "subscriptions": "[parameters('subscriptions')]" + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed profile." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters/profiles/accessRules', parameters('networkPerimeterName'), parameters('networkPerimeterProfileName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed profile." + }, + "value": "[parameters('name')]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security perimeter was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed profile." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityPerimeters/profiles', parameters('networkPerimeterName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed profile." + }, + "value": "[parameters('name')]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-security-perimeter/tests/e2e/defaults/main.test.bicep b/avm/res/network/network-security-perimeter/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..f6149b6dbe --- /dev/null +++ b/avm/res/network/network-security-perimeter/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,48 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.networksecurityperimeter-${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 = 'nnspmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-07-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' + location: resourceLocation + } + } +] diff --git a/avm/res/network/network-security-perimeter/tests/e2e/max/dependencies.bicep b/avm/res/network/network-security-perimeter/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..7c4ce12b47 --- /dev/null +++ b/avm/res/network/network-security-perimeter/tests/e2e/max/dependencies.bicep @@ -0,0 +1,36 @@ +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + allowBlobPublicAccess: false + publicNetworkAccess: 'Disabled' + networkAcls: { + defaultAction: 'Allow' + bypass: 'AzureServices' + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/network/network-security-perimeter/tests/e2e/max/main.test.bicep b/avm/res/network/network-security-perimeter/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..020eb083a9 --- /dev/null +++ b/avm/res/network/network-security-perimeter/tests/e2e/max/main.test.bicep @@ -0,0 +1,138 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.networksecurityperimeter-${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 = 'nnspmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + storageAccountName: 'dep${namePrefix}store${serviceShort}' + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +module diagnosticDependencies '../../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-07-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' + location: resourceLocation + profiles: [ + { + name: 'profile-01' + accessRules: [ + { + name: 'rule-outbound-01' + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + } + { + name: 'rule-inbound-01' + direction: 'Inbound' + addressPrefixes: [ + '198.168.1.0/24' + ] + } + ] + } + ] + resourceAssociations: [ + { + profile: 'profile-01' + privateLinkResource: nestedDependencies.outputs.storageAccountResourceId + accessMode: 'Learning' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + name: 'b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + } + } +] diff --git a/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..6dd99e1ed6 --- /dev/null +++ b/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,25 @@ +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + allowBlobPublicAccess: false + publicNetworkAccess: 'Disabled' + networkAcls: { + defaultAction: 'Allow' + bypass: 'AzureServices' + } + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id diff --git a/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0650a1d475 --- /dev/null +++ b/avm/res/network/network-security-perimeter/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,111 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.networksecurityperimeter-${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 = 'nnspwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + storageAccountName: 'dep${namePrefix}store${serviceShort}' + location: resourceLocation + } +} + +module diagnosticDependencies '../../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-07-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' + location: resourceLocation + profiles: [ + { + name: 'profile-01' + accessRules: [ + { + name: 'rule-outbound-01' + direction: 'Outbound' + fullyQualifiedDomainNames: [ + 'www.contoso.com' + ] + } + { + name: 'rule-inbound-01' + direction: 'Inbound' + addressPrefixes: [ + '198.168.1.0/24' + ] + } + ] + } + ] + resourceAssociations: [ + { + profile: 'profile-01' + privateLinkResource: nestedDependencies.outputs.storageAccountResourceId + accessMode: 'Learning' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/network-security-perimeter/version.json b/avm/res/network/network-security-perimeter/version.json new file mode 100644 index 0000000000..83083db694 --- /dev/null +++ b/avm/res/network/network-security-perimeter/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file From e6b5ab3ce58776fc83258b21dadd547fe849b676 Mon Sep 17 00:00:00 2001 From: mjrich19 <19920112+mjrich19@users.noreply.github.com> Date: Sat, 22 Feb 2025 09:16:03 -0500 Subject: [PATCH 49/53] feat: avm\res\network\virtual-network Unorphan module (#4495) Claiming the virtual network module and removing orphaned label ## Description <!-- >Thank you for your contribution ! > Please include a summary of the change and which issue is fixed. > Please also include the context. > List any dependencies that are required for this change. --> [Closes #1504](https://github.com/Azure/Azure-Verified-Modules/issues/1504) ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] 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`. - [ X ] 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. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings <!-- Please keep up to date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --- avm/res/network/virtual-network/ORPHANED.md | 4 ---- avm/res/network/virtual-network/README.md | 5 ----- avm/res/network/virtual-network/main.json | 16 ++++++++-------- avm/res/network/virtual-network/subnet/main.json | 4 ++-- .../virtual-network-peering/main.json | 4 ++-- 5 files changed, 12 insertions(+), 21 deletions(-) delete mode 100644 avm/res/network/virtual-network/ORPHANED.md diff --git a/avm/res/network/virtual-network/ORPHANED.md b/avm/res/network/virtual-network/ORPHANED.md deleted file mode 100644 index ef8fa911d2..0000000000 --- a/avm/res/network/virtual-network/ORPHANED.md +++ /dev/null @@ -1,4 +0,0 @@ -⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ - -- Only security and bug fixes are being handled by the AVM core team at present. -- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/res/network/virtual-network/README.md b/avm/res/network/virtual-network/README.md index c00fb50138..8554fbed10 100644 --- a/avm/res/network/virtual-network/README.md +++ b/avm/res/network/virtual-network/README.md @@ -1,10 +1,5 @@ # Virtual Networks `[Microsoft.Network/virtualNetworks]` -> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ -> -> - Only security and bug fixes are being handled by the AVM core team at present. -> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! - This module deploys a Virtual Network (vNet). ## Navigation diff --git a/avm/res/network/virtual-network/main.json b/avm/res/network/virtual-network/main.json index e81d0e5bb2..c142568efc 100644 --- a/avm/res/network/virtual-network/main.json +++ b/avm/res/network/virtual-network/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2376075998560091019" + "version": "0.33.93.31351", + "templateHash": "3287254436599351961" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet)." @@ -825,8 +825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6924527964815690853" + "version": "0.33.93.31351", + "templateHash": "1989433230243572453" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet." @@ -1206,8 +1206,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6667873480569726785" + "version": "0.33.93.31351", + "templateHash": "13644695472933819194" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering." @@ -1363,8 +1363,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6667873480569726785" + "version": "0.33.93.31351", + "templateHash": "13644695472933819194" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering." diff --git a/avm/res/network/virtual-network/subnet/main.json b/avm/res/network/virtual-network/subnet/main.json index 44761e1d2b..41e7eff799 100644 --- a/avm/res/network/virtual-network/subnet/main.json +++ b/avm/res/network/virtual-network/subnet/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6924527964815690853" + "version": "0.33.93.31351", + "templateHash": "1989433230243572453" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet." diff --git a/avm/res/network/virtual-network/virtual-network-peering/main.json b/avm/res/network/virtual-network/virtual-network-peering/main.json index ee192848f9..cc021df0fd 100644 --- a/avm/res/network/virtual-network/virtual-network-peering/main.json +++ b/avm/res/network/virtual-network/virtual-network-peering/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6667873480569726785" + "version": "0.33.93.31351", + "templateHash": "13644695472933819194" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering." From ed2a05285434e3d621213c6a578f6c13c89411dd Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Fri, 7 Feb 2025 08:36:19 +0100 Subject: [PATCH 50/53] Initial commit for hybrid-compute/license module --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + avm/res/hybrid-compute/license/main.bicep | 9 ++++----- avm/res/hybrid-compute/license/main.json | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 783616851d..773c2b3657 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -93,6 +93,7 @@ /avm/res/health-bot/health-bot/ @Azure/avm-res-healthbot-healthbot-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/healthcare-apis/workspace/ @Azure/avm-res-healthcareapis-workspace-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/hybrid-compute/gateway/ @Azure/avm-res-hybridcompute-gateway-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/hybrid-compute/license/ @Azure/avm-res-hybridcompute-license-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/hybrid-compute/machine/ @Azure/avm-res-hybridcompute-machine-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/action-group/ @Azure/avm-res-insights-actiongroup-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/insights/activity-log-alert/ @Azure/avm-res-insights-activitylogalert-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index ec2688d5d9..8f2e360b8b 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -128,6 +128,7 @@ body: - "avm/res/health-bot/health-bot" - "avm/res/healthcare-apis/workspace" - "avm/res/hybrid-compute/gateway" + - "avm/res/hybrid-compute/license" - "avm/res/hybrid-compute/machine" - "avm/res/insights/action-group" - "avm/res/insights/activity-log-alert" diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep index 84c38955ab..2192491125 100644 --- a/avm/res/hybrid-compute/license/main.bicep +++ b/avm/res/hybrid-compute/license/main.bicep @@ -14,11 +14,10 @@ param location string = resourceGroup().location ]) param licenseDetailEdition string = 'Standard' -@description('Optional. Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license.') -@minValue(8) -param licenseDetailProcessors int = 8 +@description('Optional. Describes the number of processors.') +param licenseDetailProcessors int = 2 -@description('Optional. Activate or Deactivate the license billing cycle. Billing will not start until the license is activated.') +@description('Optional. Describes the license state.') @allowed([ 'Active' 'Deactivated' @@ -57,7 +56,7 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true -resource license 'Microsoft.HybridCompute/licenses@2024-07-31-preview' = { +resource license 'Microsoft.HybridCompute/licenses@2024-11-10-preview' = { name: name location: location properties: { diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index cbdfd6ba7f..496f6a303b 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -218,4 +218,4 @@ "value": "[reference('license', '2024-07-31-preview', 'full').location]" } } -} \ No newline at end of file +} From 5013a12234b5672843c33442168024c93805f2bd Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Sun, 23 Feb 2025 17:48:26 +0100 Subject: [PATCH 51/53] update version --- avm/res/hybrid-compute/license/README.md | 23 ++++++----------------- avm/res/hybrid-compute/license/main.json | 15 +++++++-------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md index 676a4ba112..e7cdac9435 100644 --- a/avm/res/hybrid-compute/license/README.md +++ b/avm/res/hybrid-compute/license/README.md @@ -14,7 +14,7 @@ This module deploys an Azure Arc License for use with Azure Arc-enabled servers. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.HybridCompute/licenses` | [2024-07-31-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-07-31-preview/licenses) | +| `Microsoft.HybridCompute/licenses` | [2024-11-10-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/2024-11-10-preview/licenses) | ## Usage examples @@ -147,8 +147,8 @@ param name = 'hclwaf001' | :-- | :-- | :-- | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`licenseDetailEdition`](#parameter-licensedetailedition) | string | Describes the edition of the license. The values are either Standard or Datacenter.. | -| [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license. | -| [`licenseDetailState`](#parameter-licensedetailstate) | string | Activate or Deactivate the license billing cycle. Billing will not start until the license is activated. | +| [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Describes the number of processors. | +| [`licenseDetailState`](#parameter-licensedetailstate) | string | Describes the license state. | | [`licenseDetailTarget`](#parameter-licensedetailtarget) | string | Describes the license target server. | | [`licenseDetailType`](#parameter-licensedetailtype) | string | Provide the core type (vCore or pCore) needed for this ESU licens. | | [`licenseType`](#parameter-licensetype) | string | The type of the license resource. The value is ESU. | @@ -189,16 +189,15 @@ Describes the edition of the license. The values are either Standard or Datacent ### Parameter: `licenseDetailProcessors` -Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license. +Describes the number of processors. - Required: No - Type: int -- Default: `8` -- MinValue: 8 +- Default: `2` ### Parameter: `licenseDetailState` -Activate or Deactivate the license billing cycle. Billing will not start until the license is activated. +Describes the license state. - Required: No - Type: string @@ -210,7 +209,6 @@ Activate or Deactivate the license billing cycle. Billing will not start until t 'Deactivated' ] ``` -- MinValue: 8 ### Parameter: `licenseDetailTarget` @@ -226,7 +224,6 @@ Describes the license target server. 'Windows Server 2012 R2' ] ``` -- MinValue: 8 ### Parameter: `licenseDetailType` @@ -242,7 +239,6 @@ Provide the core type (vCore or pCore) needed for this ESU licens. 'vCore' ] ``` -- MinValue: 8 ### Parameter: `licenseType` @@ -257,7 +253,6 @@ The type of the license resource. The value is ESU. 'ESU' ] ``` -- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails` @@ -266,7 +261,6 @@ A list of volume license details. - Required: No - Type: array - Default: `[]` -- MinValue: 8 **Required parameters** @@ -281,7 +275,6 @@ The invoice id for the volume license. - Required: Yes - Type: string -- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails.programYear` @@ -297,7 +290,6 @@ Describes the program year the volume license is for. 'Year 3' ] ``` -- MinValue: 8 ### Parameter: `location` @@ -306,7 +298,6 @@ The location of the Azure Arc License to be created. - Required: No - Type: string - Default: `[resourceGroup().location]` -- MinValue: 8 ### Parameter: `tags` @@ -314,7 +305,6 @@ Tags of the resource. - Required: No - Type: object -- MinValue: 8 ### Parameter: `tenantId` @@ -323,7 +313,6 @@ The tenant ID of the license resource. Default is the tenant ID of the current s - Required: No - Type: string - Default: `[tenant().tenantId]` -- MinValue: 8 ## Outputs diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index 496f6a303b..8f6c2b219b 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "11101073144149258419" + "templateHash": "11246082834080456613" }, "name": "Azure Arc License", "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." @@ -66,10 +66,9 @@ }, "licenseDetailProcessors": { "type": "int", - "defaultValue": 8, - "minValue": 8, + "defaultValue": 2, "metadata": { - "description": "Optional. Provide the amount needed for this ESU licens. The minimum number of cores is 16 for physical and 8 for virtual core license." + "description": "Optional. Describes the number of processors." } }, "licenseDetailState": { @@ -80,7 +79,7 @@ "Deactivated" ], "metadata": { - "description": "Optional. Activate or Deactivate the license billing cycle. Billing will not start until the license is activated." + "description": "Optional. Describes the license state." } }, "licenseDetailTarget": { @@ -150,7 +149,7 @@ "resources": { "license": { "type": "Microsoft.HybridCompute/licenses", - "apiVersion": "2024-07-31-preview", + "apiVersion": "2024-11-10-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", "properties": { @@ -215,7 +214,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('license', '2024-07-31-preview', 'full').location]" + "value": "[reference('license', '2024-11-10-preview', 'full').location]" } } -} +} \ No newline at end of file From ab498f57bb4ea94898a255e74fefb23a96fa64d0 Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Sun, 23 Feb 2025 17:56:20 +0100 Subject: [PATCH 52/53] Fixing various bugs --- avm/res/hybrid-compute/license/main.bicep | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/avm/res/hybrid-compute/license/main.bicep b/avm/res/hybrid-compute/license/main.bicep index 2192491125..4154b39e6d 100644 --- a/avm/res/hybrid-compute/license/main.bicep +++ b/avm/res/hybrid-compute/license/main.bicep @@ -7,7 +7,7 @@ param name string @description('Optional. The location of the Azure Arc License to be created.') param location string = resourceGroup().location -@description('Optional. Describes the edition of the license. The values are either Standard or Datacenter..') +@description('Optional. Describes the edition of the license. Default is Standard.') @allowed([ 'Standard' 'Datacenter' @@ -15,23 +15,24 @@ param location string = resourceGroup().location param licenseDetailEdition string = 'Standard' @description('Optional. Describes the number of processors.') -param licenseDetailProcessors int = 2 +@minValue(8) +param licenseDetailProcessors int = 8 -@description('Optional. Describes the license state.') +@description('Optional. Describes the license state. Default is Deactivated.') @allowed([ 'Active' 'Deactivated' ]) param licenseDetailState string = 'Deactivated' -@description('Optional. Describes the license target server.') +@description('Optional. Describes the license target server. Default is Windows Server 2012 R2.') @allowed([ 'Windows Server 2012 R2' 'Windows Server 2012' ]) param licenseDetailTarget string = 'Windows Server 2012 R2' -@description('Optional. Provide the core type (vCore or pCore) needed for this ESU licens.') +@description('Optional. Provide the core type (vCore or pCore) needed for this ESU licens. Default is vCore.') @allowed([ 'pCore' 'vCore' From e5f0b0a70b142f5bc51ebf1a9ed7b2fa2dba8af8 Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Sun, 23 Feb 2025 18:00:34 +0100 Subject: [PATCH 53/53] Update Set-AVMModule --- avm/res/hybrid-compute/license/README.md | 29 ++++++++++++++++-------- avm/res/hybrid-compute/license/main.json | 13 ++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/avm/res/hybrid-compute/license/README.md b/avm/res/hybrid-compute/license/README.md index e7cdac9435..b0bb755977 100644 --- a/avm/res/hybrid-compute/license/README.md +++ b/avm/res/hybrid-compute/license/README.md @@ -146,11 +146,11 @@ param name = 'hclwaf001' | Parameter | Type | Description | | :-- | :-- | :-- | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`licenseDetailEdition`](#parameter-licensedetailedition) | string | Describes the edition of the license. The values are either Standard or Datacenter.. | +| [`licenseDetailEdition`](#parameter-licensedetailedition) | string | Describes the edition of the license. Default is Standard. | | [`licenseDetailProcessors`](#parameter-licensedetailprocessors) | int | Describes the number of processors. | -| [`licenseDetailState`](#parameter-licensedetailstate) | string | Describes the license state. | -| [`licenseDetailTarget`](#parameter-licensedetailtarget) | string | Describes the license target server. | -| [`licenseDetailType`](#parameter-licensedetailtype) | string | Provide the core type (vCore or pCore) needed for this ESU licens. | +| [`licenseDetailState`](#parameter-licensedetailstate) | string | Describes the license state. Default is Deactivated. | +| [`licenseDetailTarget`](#parameter-licensedetailtarget) | string | Describes the license target server. Default is Windows Server 2012 R2. | +| [`licenseDetailType`](#parameter-licensedetailtype) | string | Provide the core type (vCore or pCore) needed for this ESU licens. Default is vCore. | | [`licenseType`](#parameter-licensetype) | string | The type of the license resource. The value is ESU. | | [`licenseVolumeLicenseDetails`](#parameter-licensevolumelicensedetails) | array | A list of volume license details. | | [`location`](#parameter-location) | string | The location of the Azure Arc License to be created. | @@ -174,7 +174,7 @@ Enable/Disable usage telemetry for module. ### Parameter: `licenseDetailEdition` -Describes the edition of the license. The values are either Standard or Datacenter.. +Describes the edition of the license. Default is Standard. - Required: No - Type: string @@ -193,11 +193,12 @@ Describes the number of processors. - Required: No - Type: int -- Default: `2` +- Default: `8` +- MinValue: 8 ### Parameter: `licenseDetailState` -Describes the license state. +Describes the license state. Default is Deactivated. - Required: No - Type: string @@ -209,10 +210,11 @@ Describes the license state. 'Deactivated' ] ``` +- MinValue: 8 ### Parameter: `licenseDetailTarget` -Describes the license target server. +Describes the license target server. Default is Windows Server 2012 R2. - Required: No - Type: string @@ -224,10 +226,11 @@ Describes the license target server. 'Windows Server 2012 R2' ] ``` +- MinValue: 8 ### Parameter: `licenseDetailType` -Provide the core type (vCore or pCore) needed for this ESU licens. +Provide the core type (vCore or pCore) needed for this ESU licens. Default is vCore. - Required: No - Type: string @@ -239,6 +242,7 @@ Provide the core type (vCore or pCore) needed for this ESU licens. 'vCore' ] ``` +- MinValue: 8 ### Parameter: `licenseType` @@ -253,6 +257,7 @@ The type of the license resource. The value is ESU. 'ESU' ] ``` +- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails` @@ -261,6 +266,7 @@ A list of volume license details. - Required: No - Type: array - Default: `[]` +- MinValue: 8 **Required parameters** @@ -275,6 +281,7 @@ The invoice id for the volume license. - Required: Yes - Type: string +- MinValue: 8 ### Parameter: `licenseVolumeLicenseDetails.programYear` @@ -290,6 +297,7 @@ Describes the program year the volume license is for. 'Year 3' ] ``` +- MinValue: 8 ### Parameter: `location` @@ -298,6 +306,7 @@ The location of the Azure Arc License to be created. - Required: No - Type: string - Default: `[resourceGroup().location]` +- MinValue: 8 ### Parameter: `tags` @@ -305,6 +314,7 @@ Tags of the resource. - Required: No - Type: object +- MinValue: 8 ### Parameter: `tenantId` @@ -313,6 +323,7 @@ The tenant ID of the license resource. Default is the tenant ID of the current s - Required: No - Type: string - Default: `[tenant().tenantId]` +- MinValue: 8 ## Outputs diff --git a/avm/res/hybrid-compute/license/main.json b/avm/res/hybrid-compute/license/main.json index 8f6c2b219b..fb247179ec 100644 --- a/avm/res/hybrid-compute/license/main.json +++ b/avm/res/hybrid-compute/license/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "11246082834080456613" + "templateHash": "15046234377831925786" }, "name": "Azure Arc License", "description": "This module deploys an Azure Arc License for use with Azure Arc-enabled servers. This module should not be used for other Arc-enabled server scenarios, where the Arc License resource is created automatically by the onboarding process." @@ -61,12 +61,13 @@ "Datacenter" ], "metadata": { - "description": "Optional. Describes the edition of the license. The values are either Standard or Datacenter.." + "description": "Optional. Describes the edition of the license. Default is Standard." } }, "licenseDetailProcessors": { "type": "int", - "defaultValue": 2, + "defaultValue": 8, + "minValue": 8, "metadata": { "description": "Optional. Describes the number of processors." } @@ -79,7 +80,7 @@ "Deactivated" ], "metadata": { - "description": "Optional. Describes the license state." + "description": "Optional. Describes the license state. Default is Deactivated." } }, "licenseDetailTarget": { @@ -90,7 +91,7 @@ "Windows Server 2012" ], "metadata": { - "description": "Optional. Describes the license target server." + "description": "Optional. Describes the license target server. Default is Windows Server 2012 R2." } }, "licenseDetailType": { @@ -101,7 +102,7 @@ "vCore" ], "metadata": { - "description": "Optional. Provide the core type (vCore or pCore) needed for this ESU licens." + "description": "Optional. Provide the core type (vCore or pCore) needed for this ESU licens. Default is vCore." } }, "licenseVolumeLicenseDetails": {