diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 574633dafa..ba44df3386 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -62,6 +62,7 @@ /avm/res/event-hub/namespace/ @Azure/avm-res-eventhub-namespace-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/health-bot/health-bot/ @Azure/avm-res-healthbot-healthbot-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/healthcare-apis/workspace/ @Azure/avm-res-healthcareapis-workspace-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/hybrid-compute/machine/ @Azure/avm-res-hybridcompute-machine-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/insights/action-group/ @Azure/avm-res-insights-actiongroup-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/insights/activity-log-alert/ @Azure/avm-res-insights-activitylogalert-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/insights/component/ @Azure/avm-res-insights-component-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 4813ece366..7230080f1f 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -97,6 +97,7 @@ body: - "avm/res/event-hub/namespace" - "avm/res/health-bot/health-bot" - "avm/res/healthcare-apis/workspace" + - "avm/res/hybrid-compute/machine" - "avm/res/insights/action-group" - "avm/res/insights/activity-log-alert" - "avm/res/insights/component" diff --git a/.github/policies/eventResponder.yml b/.github/policies/eventResponder.yml index 516806ee93..ca47bcabc9 100644 --- a/.github/policies/eventResponder.yml +++ b/.github/policies/eventResponder.yml @@ -192,3 +192,19 @@ configuration: then: - addLabel: label: "Type: Security Bug :lock:" + + - description: "ITA25 - If a new PR is opened, inform module owners about the usage of the Needs Core Team label" + if: + - or: + - payloadType: Pull_Request + - isAction: + action: Opened + then: + - addReply: + reply: | + > [!IMPORTANT] + > If this is a module-related PR, being submitted by the sole owner of the module, the AVM core team must review and approve it (as module owners can't approve their own PRs). + > + > **To indicate this PR needs the core team''s attention, apply the "Needs: Core Team 🧞" label!** + > + > The core team will only review and approve PRs that have this label applied! diff --git a/.github/workflows/avm.res.hybrid-compute.machine.yml b/.github/workflows/avm.res.hybrid-compute.machine.yml new file mode 100644 index 0000000000..1b37947d8e --- /dev/null +++ b/.github/workflows/avm.res.hybrid-compute.machine.yml @@ -0,0 +1,90 @@ +name: "avm.res.hybrid-compute.machine" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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.machine.yml" + - "avm/res/hybrid-compute/machine/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/hybrid-compute/machine" + workflowPath: ".github/workflows/avm.res.hybrid-compute.machine.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/.github/workflows/avm.res.kusto.cluster.yml b/.github/workflows/avm.res.kusto.cluster.yml index 2811d32291..1b8404997e 100644 --- a/.github/workflows/avm.res.kusto.cluster.yml +++ b/.github/workflows/avm.res.kusto.cluster.yml @@ -1,8 +1,6 @@ name: "avm.res.kusto.cluster" on: - schedule: - - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) workflow_dispatch: inputs: staticValidation: diff --git a/.github/workflows/platform.ossf-scorecard.yml b/.github/workflows/platform.ossf-scorecard.yml new file mode 100644 index 0000000000..f0f6af0845 --- /dev/null +++ b/.github/workflows/platform.ossf-scorecard.yml @@ -0,0 +1,73 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: .Platform - OSSF Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '17 17 * * 1' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + sarif_file: results.sarif diff --git a/README.md b/README.md index a3b009c974..b508e6163a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Azure/bicep-registry-modules/badge)](https://scorecard.dev/viewer/?uri=github.com/Azure/bicep-registry-modules) +

⚠️ New standard for Bicep modules - AVM ⚠️

To provide our customers with a unified experience, Azure Verified Modules ([AVM](https://aka.ms/AVM)) is now the single Microsoft standard for Bicep modules, published to the Public Bicep Registry, via this repository. diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 1815d28244..9d90d9d141 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -236,6 +236,11 @@ The domain name specific to the Azure ADDS service. - Required: Yes - Type: string +- Example: + ```Bicep + - 'contoso.onmicrosoft.com' + - 'aaddscontoso.com' + ``` ### Parameter: `pfxCertificate` @@ -260,6 +265,11 @@ The email recipient value to receive alerts. - Required: No - Type: array - Default: `[]` +- Example: + ```Bicep + - ['john@doh.org'] + - ['john@doh.org','jane@doh.org'] + ``` ### Parameter: `diagnosticSettings` @@ -629,6 +639,7 @@ The id of the subnet that Domain Services will be deployed on. The subnet has so - Required: Yes - Type: string +- Example: `/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/` ### Parameter: `roleAssignments` @@ -771,6 +782,13 @@ Tags of the resource. - Required: No - Type: object +- Example: + ```Bicep + { + "key1": "value1", + "key2": "value2" + } + ``` ### Parameter: `tlsV1` diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index 82650f1d97..a9c4633306 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -141,9 +141,9 @@ module service 'br/public:avm/res/api-management/service:' = { apiDiagnostics: [ { apiName: 'echo-api' - diagnosticName: 'applicationinsights' loggerName: 'logger' metrics: true + name: 'applicationinsights' } ] apis: [ @@ -369,9 +369,9 @@ module service 'br/public:avm/res/api-management/service:' = { "value": [ { "apiName": "echo-api", - "diagnosticName": "applicationinsights", "loggerName": "logger", - "metrics": true + "metrics": true, + "name": "applicationinsights" } ] }, @@ -1146,8 +1146,8 @@ module service 'br/public:avm/res/api-management/service:' = { | [`publicIpAddressResourceId`](#parameter-publicipaddressresourceid) | string | Public Standard SKU IP V4 based IP address to be associated with Virtual Network deployed service in the region. Supported only for Developer and Premium SKU being deployed in Virtual Network. | | [`restore`](#parameter-restore) | bool | Undelete API Management Service if it was previously soft-deleted. If this flag is specified and set to True all other properties will be ignored. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | -| [`sku`](#parameter-sku) | string | The pricing tier of this API Management service. Default is Premium. | -| [`skuCount`](#parameter-skucount) | int | The instance size of this API Management service. Default is 2. Not supported with V2 SKUs. If using Consumption, sku should = 0. | +| [`sku`](#parameter-sku) | string | The pricing tier of this API Management service. | +| [`skuCount`](#parameter-skucount) | int | The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0. | | [`subnetResourceId`](#parameter-subnetresourceid) | string | The full resource ID of a subnet in a virtual network to deploy the API Management service in. | | [`subscriptions`](#parameter-subscriptions) | array | Subscriptions. | | [`tags`](#parameter-tags) | object | Tags of the resource. | @@ -1686,7 +1686,7 @@ The principal type of the assigned principal ID. ### Parameter: `sku` -The pricing tier of this API Management service. Default is Premium. +The pricing tier of this API Management service. - Required: No - Type: string @@ -1706,7 +1706,7 @@ The pricing tier of this API Management service. Default is Premium. ### Parameter: `skuCount` -The instance size of this API Management service. Default is 2. Not supported with V2 SKUs. If using Consumption, sku should = 0. +The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0. - Required: No - Type: int diff --git a/avm/res/api-management/service/api/README.md b/avm/res/api-management/service/api/README.md index 39c0a64a6c..2f5d3eb04e 100644 --- a/avm/res/api-management/service/api/README.md +++ b/avm/res/api-management/service/api/README.md @@ -24,15 +24,16 @@ This module deploys an API Management Service API. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`apiName`](#parameter-apiname) | string | API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number. | -| [`apiPath`](#parameter-apipath) | string | Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. | | [`displayName`](#parameter-displayname) | string | API name. Must be 1 to 300 characters long. | +| [`name`](#parameter-name) | string | API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number. | +| [`path`](#parameter-path) | string | Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. | **Conditional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`apiManagementServiceName`](#parameter-apimanagementservicename) | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | +| [`loggerName`](#parameter-loggername) | string | The name of the API management service logger. Required if using api/diagnostics. | **Optional parameters** @@ -49,7 +50,6 @@ This module deploys an API Management Service API. | [`diagnostics`](#parameter-diagnostics) | array | Array of diagnostics to apply to the Service API. | | [`format`](#parameter-format) | string | Format of the Content in which the API is getting imported. | | [`isCurrent`](#parameter-iscurrent) | bool | Indicates if API revision is current API revision. | -| [`loggerName`](#parameter-loggername) | string | The name of the API management service logger. | | [`policies`](#parameter-policies) | array | Array of Policies to apply to the Service API. | | [`protocols`](#parameter-protocols) | array | Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS. | | [`serviceUrl`](#parameter-serviceurl) | string | Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters long. | @@ -60,23 +60,23 @@ This module deploys an API Management Service API. | [`value`](#parameter-value) | string | Content value when Importing an API. | | [`wsdlSelector`](#parameter-wsdlselector) | object | Criteria to limit import of WSDL to a subset of the document. | -### Parameter: `apiName` +### Parameter: `displayName` -API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number. +API name. Must be 1 to 300 characters long. - Required: Yes - Type: string -### Parameter: `apiPath` +### Parameter: `name` -Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. +API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number. - Required: Yes - Type: string -### Parameter: `displayName` +### Parameter: `path` -API name. Must be 1 to 300 characters long. +Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. - Required: Yes - Type: string @@ -88,6 +88,14 @@ The name of the parent API Management service. Required if the template is used - Required: Yes - Type: string +### Parameter: `loggerName` + +The name of the API management service logger. Required if using api/diagnostics. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `apiDescription` Description of the API. May include HTML formatting tags. @@ -192,14 +200,6 @@ Indicates if API revision is current API revision. - Type: bool - Default: `True` -### Parameter: `loggerName` - -The name of the API management service logger. - -- Required: No -- Type: string -- Default: `''` - ### Parameter: `policies` Array of Policies to apply to the Service API. diff --git a/avm/res/api-management/service/api/diagnostics/README.md b/avm/res/api-management/service/api/diagnostics/README.md index c3305aa3b3..d7d6615ee9 100644 --- a/avm/res/api-management/service/api/diagnostics/README.md +++ b/avm/res/api-management/service/api/diagnostics/README.md @@ -30,9 +30,9 @@ This module deploys an API Management Service API Diagnostics. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`httpCorrelationProtocol`](#parameter-httpcorrelationprotocol) | string | Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights. | -| [`metrics`](#parameter-metrics) | bool | Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights. | -| [`operationNameFormat`](#parameter-operationnameformat) | string | The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights. | +| [`httpCorrelationProtocol`](#parameter-httpcorrelationprotocol) | string | Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights. | +| [`metrics`](#parameter-metrics) | bool | Emit custom metrics via emit-metric policy. Required if using Application Insights. | +| [`operationNameFormat`](#parameter-operationnameformat) | string | The format of the Operation Name for Application Insights telemetries. Required if using Application Insights. | **Optional parameters** @@ -40,11 +40,11 @@ This module deploys an API Management Service API Diagnostics. | :-- | :-- | :-- | | [`alwaysLog`](#parameter-alwayslog) | string | Specifies for what type of messages sampling settings should not apply. | | [`backend`](#parameter-backend) | object | Diagnostic settings for incoming/outgoing HTTP messages to the Backend. | -| [`diagnosticName`](#parameter-diagnosticname) | string | Type of diagnostic resource. Default is local. | | [`frontend`](#parameter-frontend) | object | Diagnostic settings for incoming/outgoing HTTP messages to the Gateway. | -| [`logClientIp`](#parameter-logclientip) | bool | Log the ClientIP. Default is false. | -| [`samplingPercentage`](#parameter-samplingpercentage) | int | Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100. | -| [`verbosity`](#parameter-verbosity) | string | The verbosity level applied to traces emitted by trace policies. Default is "error". | +| [`logClientIp`](#parameter-logclientip) | bool | Log the ClientIP. | +| [`name`](#parameter-name) | string | Type of diagnostic resource. | +| [`samplingPercentage`](#parameter-samplingpercentage) | int | Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. | +| [`verbosity`](#parameter-verbosity) | string | The verbosity level applied to traces emitted by trace policies. | ### Parameter: `apiManagementServiceName` @@ -69,7 +69,7 @@ The name of the logger. ### Parameter: `httpCorrelationProtocol` -Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights. +Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights. - Required: No - Type: string @@ -85,7 +85,7 @@ Sets correlation protocol to use for Application Insights diagnostics. Default i ### Parameter: `metrics` -Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights. +Emit custom metrics via emit-metric policy. Required if using Application Insights. - Required: No - Type: bool @@ -93,7 +93,7 @@ Emit custom metrics via emit-metric policy. Default is false. Required if using ### Parameter: `operationNameFormat` -The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights. +The format of the Operation Name for Application Insights telemetries. Required if using Application Insights. - Required: No - Type: string @@ -122,22 +122,6 @@ Diagnostic settings for incoming/outgoing HTTP messages to the Backend. - Type: object - Default: `{}` -### Parameter: `diagnosticName` - -Type of diagnostic resource. Default is local. - -- Required: No -- Type: string -- Default: `'local'` -- Allowed: - ```Bicep - [ - 'applicationinsights' - 'azuremonitor' - 'local' - ] - ``` - ### Parameter: `frontend` Diagnostic settings for incoming/outgoing HTTP messages to the Gateway. @@ -148,15 +132,31 @@ Diagnostic settings for incoming/outgoing HTTP messages to the Gateway. ### Parameter: `logClientIp` -Log the ClientIP. Default is false. +Log the ClientIP. - Required: No - Type: bool - Default: `False` +### Parameter: `name` + +Type of diagnostic resource. + +- Required: No +- Type: string +- Default: `'local'` +- Allowed: + ```Bicep + [ + 'applicationinsights' + 'azuremonitor' + 'local' + ] + ``` + ### Parameter: `samplingPercentage` -Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100. +Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. - Required: No - Type: int @@ -164,7 +164,7 @@ Rate of sampling for fixed-rate sampling. Specifies the percentage of requests t ### Parameter: `verbosity` -The verbosity level applied to traces emitted by trace policies. Default is "error". +The verbosity level applied to traces emitted by trace policies. - Required: No - Type: string diff --git a/avm/res/api-management/service/api/diagnostics/main.bicep b/avm/res/api-management/service/api/diagnostics/main.bicep index a96ca28ff2..c0693b4981 100644 --- a/avm/res/api-management/service/api/diagnostics/main.bicep +++ b/avm/res/api-management/service/api/diagnostics/main.bicep @@ -16,8 +16,8 @@ param loggerName string 'applicationinsights' 'local' ]) -@description('Optional. Type of diagnostic resource. Default is local.') -param diagnosticName string = 'local' +@description('Optional. Type of diagnostic resource.') +param name string = 'local' @description('Optional. Specifies for what type of messages sampling settings should not apply.') param alwaysLog string = 'allErrors' @@ -33,23 +33,23 @@ param frontend object = {} 'None' 'W3C' ]) -@description('Conditional. Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights.') +@description('Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights.') param httpCorrelationProtocol string = 'Legacy' -@description('Optional. Log the ClientIP. Default is false.') +@description('Optional. Log the ClientIP.') param logClientIp bool = false -@description('Conditional. Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights.') +@description('Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights.') param metrics bool = false @allowed([ 'Name' 'URI' ]) -@description('Conditional. The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights.') +@description('Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights.') param operationNameFormat string = 'Name' -@description('Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100.') +@description('Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged.') param samplingPercentage int = 100 @allowed([ @@ -57,7 +57,7 @@ param samplingPercentage int = 100 'information' 'verbose' ]) -@description('Optional. The verbosity level applied to traces emitted by trace policies. Default is "error".') +@description('Optional. The verbosity level applied to traces emitted by trace policies.') param verbosity string = 'error' resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { @@ -73,7 +73,7 @@ resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { } resource diagnostic 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = { - name: diagnosticName + name: name parent: service::api properties: { alwaysLog: alwaysLog diff --git a/avm/res/api-management/service/api/diagnostics/main.json b/avm/res/api-management/service/api/diagnostics/main.json index f6a9a44949..07dc670cb4 100644 --- a/avm/res/api-management/service/api/diagnostics/main.json +++ b/avm/res/api-management/service/api/diagnostics/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "10754963300235270688" + "templateHash": "15990218139655805007" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -30,7 +30,7 @@ "description": "Required. The name of the logger." } }, - "diagnosticName": { + "name": { "type": "string", "defaultValue": "local", "allowedValues": [ @@ -39,7 +39,7 @@ "local" ], "metadata": { - "description": "Optional. Type of diagnostic resource. Default is local." + "description": "Optional. Type of diagnostic resource." } }, "alwaysLog": { @@ -72,21 +72,21 @@ "W3C" ], "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights." + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." } }, "logClientIp": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Log the ClientIP. Default is false." + "description": "Optional. Log the ClientIP." } }, "metrics": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights." + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." } }, "operationNameFormat": { @@ -97,14 +97,14 @@ "URI" ], "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights." + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." } }, "samplingPercentage": { "type": "int", "defaultValue": 100, "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100." + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." } }, "verbosity": { @@ -116,7 +116,7 @@ "verbose" ], "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies. Default is \"error\"." + "description": "Optional. The verbosity level applied to traces emitted by trace policies." } } }, @@ -124,7 +124,7 @@ { "type": "Microsoft.ApiManagement/service/apis/diagnostics", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", "properties": { "alwaysLog": "[parameters('alwaysLog')]", "backend": "[parameters('backend')]", @@ -148,14 +148,14 @@ "metadata": { "description": "The resource ID of the API diagnostic." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" }, "name": { "type": "string", "metadata": { "description": "The name of the API diagnostic." }, - "value": "[parameters('diagnosticName')]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/api-management/service/api/main.bicep b/avm/res/api-management/service/api/main.bicep index 0ae8f8ccde..f3cf6a2b3c 100644 --- a/avm/res/api-management/service/api/main.bicep +++ b/avm/res/api-management/service/api/main.bicep @@ -3,7 +3,7 @@ metadata description = 'This module deploys an API Management Service API.' metadata owner = 'Azure/module-maintainers' @description('Required. API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number.') -param apiName string +param name string @description('Optional. Array of Policies to apply to the Service API.') param policies array? @@ -66,11 +66,11 @@ param format string = 'openapi' @description('Optional. Indicates if API revision is current API revision.') param isCurrent bool = true -@description('Optional. The name of the API management service logger.') +@description('Conditional. The name of the API management service logger. Required if using api/diagnostics.') param loggerName string = '' @description('Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') -param apiPath string +param path string @description('Optional. Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS.') param protocols array = [ @@ -110,7 +110,7 @@ resource service 'Microsoft.ApiManagement/service@2023-05-01-preview' existing = } resource api 'Microsoft.ApiManagement/service/apis@2022-08-01' = { - name: apiName + name: name parent: service properties: { apiRevision: apiRevision @@ -124,7 +124,7 @@ resource api 'Microsoft.ApiManagement/service/apis@2022-08-01' = { displayName: displayName format: !empty(value) ? format : null isCurrent: isCurrent - path: apiPath + path: path protocols: protocols serviceUrl: serviceUrl sourceApiId: sourceApiId @@ -152,7 +152,7 @@ module diagnostic 'diagnostics/main.bicep' = [ for (diagnostic, index) in diagnostics ?? []: { name: '${deployment().name}-diagnostics-${index}' params: { - diagnosticName: !empty(diagnostic.diagnosticName) ? diagnostic.diagnosticName : '${api.name}-diagnostics-${index}' + name: diagnostic.?name apiManagementServiceName: apiManagementServiceName apiName: api.name loggerName: loggerName diff --git a/avm/res/api-management/service/api/main.json b/avm/res/api-management/service/api/main.json index af1a3bdaca..151111afb0 100644 --- a/avm/res/api-management/service/api/main.json +++ b/avm/res/api-management/service/api/main.json @@ -6,14 +6,14 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "8244071930603774060" + "templateHash": "4127687153063244658" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", "owner": "Azure/module-maintainers" }, "parameters": { - "apiName": { + "name": { "type": "string", "metadata": { "description": "Required. API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number." @@ -138,10 +138,10 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. The name of the API management service logger." + "description": "Conditional. The name of the API management service logger. Required if using api/diagnostics." } }, - "apiPath": { + "path": { "type": "string", "metadata": { "description": "Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." @@ -223,7 +223,7 @@ "api": { "type": "Microsoft.ApiManagement/service/apis", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('apiName'))]", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "apiRevision": "[parameters('apiRevision')]", "apiRevisionDescription": "[parameters('apiRevisionDescription')]", @@ -236,7 +236,7 @@ "displayName": "[parameters('displayName')]", "format": "[if(not(empty(parameters('value'))), parameters('format'), null())]", "isCurrent": "[parameters('isCurrent')]", - "path": "[parameters('apiPath')]", + "path": "[parameters('path')]", "protocols": "[parameters('protocols')]", "serviceUrl": "[parameters('serviceUrl')]", "sourceApiId": "[parameters('sourceApiId')]", @@ -268,7 +268,7 @@ "value": "[parameters('apiManagementServiceName')]" }, "apiName": { - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "format": "[if(contains(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), createObject('value', coalesce(parameters('policies'), createArray())[copyIndex()].format), createObject('value', 'xml'))]", "value": { @@ -382,12 +382,14 @@ }, "mode": "Incremental", "parameters": { - "diagnosticName": "[if(not(empty(coalesce(parameters('diagnostics'), createArray())[copyIndex()].diagnosticName)), createObject('value', coalesce(parameters('diagnostics'), createArray())[copyIndex()].diagnosticName), createObject('value', format('{0}-diagnostics-{1}', parameters('apiName'), copyIndex())))]", + "name": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'name')]" + }, "apiManagementServiceName": { "value": "[parameters('apiManagementServiceName')]" }, "apiName": { - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "loggerName": { "value": "[parameters('loggerName')]" @@ -427,7 +429,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "10754963300235270688" + "templateHash": "15990218139655805007" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -452,7 +454,7 @@ "description": "Required. The name of the logger." } }, - "diagnosticName": { + "name": { "type": "string", "defaultValue": "local", "allowedValues": [ @@ -461,7 +463,7 @@ "local" ], "metadata": { - "description": "Optional. Type of diagnostic resource. Default is local." + "description": "Optional. Type of diagnostic resource." } }, "alwaysLog": { @@ -494,21 +496,21 @@ "W3C" ], "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights." + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." } }, "logClientIp": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Log the ClientIP. Default is false." + "description": "Optional. Log the ClientIP." } }, "metrics": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights." + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." } }, "operationNameFormat": { @@ -519,14 +521,14 @@ "URI" ], "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights." + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." } }, "samplingPercentage": { "type": "int", "defaultValue": 100, "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100." + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." } }, "verbosity": { @@ -538,7 +540,7 @@ "verbose" ], "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies. Default is \"error\"." + "description": "Optional. The verbosity level applied to traces emitted by trace policies." } } }, @@ -546,7 +548,7 @@ { "type": "Microsoft.ApiManagement/service/apis/diagnostics", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", "properties": { "alwaysLog": "[parameters('alwaysLog')]", "backend": "[parameters('backend')]", @@ -570,14 +572,14 @@ "metadata": { "description": "The resource ID of the API diagnostic." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" }, "name": { "type": "string", "metadata": { "description": "The name of the API diagnostic." }, - "value": "[parameters('diagnosticName')]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", @@ -600,14 +602,14 @@ "metadata": { "description": "The name of the API management service API." }, - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the API management service API." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('apiName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/api-management/service/loggers/README.md b/avm/res/api-management/service/loggers/README.md index ebbddf783b..ddc2cb1dd4 100644 --- a/avm/res/api-management/service/loggers/README.md +++ b/avm/res/api-management/service/loggers/README.md @@ -30,14 +30,14 @@ This module deploys an API Management Service Logger. | Parameter | Type | Description | | :-- | :-- | :-- | | [`apiManagementServiceName`](#parameter-apimanagementservicename) | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | -| [`credentials`](#parameter-credentials) | object | Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger. | +| [`credentials`](#parameter-credentials) | secureObject | Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger. | | [`targetResourceId`](#parameter-targetresourceid) | string | Required if loggerType = applicationInsights or azureEventHub. Azure Resource Id of a log target (either Azure Event Hub resource or Azure Application Insights resource). | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`isBuffered`](#parameter-isbuffered) | bool | Whether records are buffered in the logger before publishing. Default is assumed to be true. | +| [`isBuffered`](#parameter-isbuffered) | bool | Whether records are buffered in the logger before publishing. | | [`loggerDescription`](#parameter-loggerdescription) | string | Logger description. | ### Parameter: `loggerType` @@ -74,7 +74,7 @@ The name of the parent API Management service. Required if the template is used Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger. - Required: Yes -- Type: object +- Type: secureObject ### Parameter: `targetResourceId` @@ -85,10 +85,11 @@ Required if loggerType = applicationInsights or azureEventHub. Azure Resource Id ### Parameter: `isBuffered` -Whether records are buffered in the logger before publishing. Default is assumed to be true. +Whether records are buffered in the logger before publishing. -- Required: Yes +- Required: No - Type: bool +- Default: `True` ### Parameter: `loggerDescription` diff --git a/avm/res/api-management/service/loggers/main.bicep b/avm/res/api-management/service/loggers/main.bicep index 5733be6532..6f7d1af8fb 100644 --- a/avm/res/api-management/service/loggers/main.bicep +++ b/avm/res/api-management/service/loggers/main.bicep @@ -11,8 +11,8 @@ param name string @description('Optional. Logger description.') param loggerDescription string -@description('Optional. Whether records are buffered in the logger before publishing. Default is assumed to be true.') -param isBuffered bool +@description('Optional. Whether records are buffered in the logger before publishing.') +param isBuffered bool = true @description('Required. Logger type.') @allowed([ @@ -25,6 +25,7 @@ param loggerType string @description('Conditional. Required if loggerType = applicationInsights or azureEventHub. Azure Resource Id of a log target (either Azure Event Hub resource or Azure Application Insights resource).') param targetResourceId string +@secure() @description('Conditional. Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger.') param credentials object diff --git a/avm/res/api-management/service/loggers/main.json b/avm/res/api-management/service/loggers/main.json index a6840a3a1a..34d7e48e02 100644 --- a/avm/res/api-management/service/loggers/main.json +++ b/avm/res/api-management/service/loggers/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "11887059084876487326" + "templateHash": "11123719171758533120" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", @@ -32,8 +32,9 @@ }, "isBuffered": { "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Whether records are buffered in the logger before publishing. Default is assumed to be true." + "description": "Optional. Whether records are buffered in the logger before publishing." } }, "loggerType": { @@ -54,7 +55,7 @@ } }, "credentials": { - "type": "object", + "type": "secureObject", "metadata": { "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger." } diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index c112a70e05..f3e472efd8 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -63,7 +63,7 @@ param restore bool = false @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType -@description('Optional. The pricing tier of this API Management service. Default is Premium.') +@description('Optional. The pricing tier of this API Management service.') @allowed([ 'Consumption' 'Developer' @@ -75,7 +75,7 @@ param roleAssignments roleAssignmentType ]) param sku string = 'Premium' -@description('Optional. The instance size of this API Management service. Default is 2. Not supported with V2 SKUs. If using Consumption, sku should = 0.') +@description('Optional. The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0.') @allowed([ 0 1 @@ -259,8 +259,8 @@ module service_apis 'api/main.bicep' = [ params: { apiManagementServiceName: service.name displayName: api.displayName - apiName: api.name - apiPath: api.path + name: api.name + path: api.path apiDescription: api.?apiDescription apiRevision: api.?apiRevision apiRevisionDescription: api.?apiRevisionDescription @@ -382,19 +382,17 @@ module service_apiDiagnostics 'api/diagnostics/main.bicep' = [ params: { apiManagementServiceName: service.name apiName: apidiagnostic.apiName - loggerName: apidiagnostic.loggerName - diagnosticName: contains(apidiagnostic, 'diagnosticName') ? apidiagnostic.diagnosticName : 'local' - alwaysLog: contains(apidiagnostic, 'alwaysLog') ? apidiagnostic.alwaysLog : 'allErrors' - backend: contains(apidiagnostic, 'backend') ? apidiagnostic.backend : {} - frontend: contains(apidiagnostic, 'frontend') ? apidiagnostic.frontend : {} - httpCorrelationProtocol: contains(apidiagnostic, 'httpCorrelationProtocol') - ? apidiagnostic.httpCorrelationProtocol - : 'Legacy' - logClientIp: contains(apidiagnostic, 'logClientIp') ? apidiagnostic.logClientIp : false - metrics: contains(apidiagnostic, 'metrics') ? apidiagnostic.metrics : false - operationNameFormat: contains(apidiagnostic, 'operationNameFormat') ? apidiagnostic.operationNameFormat : 'Name' - samplingPercentage: contains(apidiagnostic, 'samplingPercentage') ? apidiagnostic.samplingPercentage : 100 - verbosity: contains(apidiagnostic, 'verbosity') ? apidiagnostic.verbosity : 'error' + loggerName: apidiagnostic.?loggerName + name: apidiagnostic.?name + alwaysLog: apidiagnostic.?alwaysLog + backend: apidiagnostic.?backend + frontend: apidiagnostic.?frontend + httpCorrelationProtocol: apidiagnostic.?httpCorrelationProtocol + logClientIp: apidiagnostic.?logClientIp + metrics: apidiagnostic.?metrics + operationNameFormat: apidiagnostic.?operationNameFormat + samplingPercentage: apidiagnostic.?samplingPercentage + verbosity: apidiagnostic.?verbosity } dependsOn: [ service_apis diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index f2f5479374..32da89d913 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "9064115171009463651" + "templateHash": "14490799655987897540" }, "name": "API Management Services", "description": "This module deploys an API Management Service.", @@ -385,7 +385,7 @@ "BasicV2" ], "metadata": { - "description": "Optional. The pricing tier of this API Management service. Default is Premium." + "description": "Optional. The pricing tier of this API Management service." } }, "skuCount": { @@ -398,7 +398,7 @@ 3 ], "metadata": { - "description": "Optional. The instance size of this API Management service. Default is 2. Not supported with V2 SKUs. If using Consumption, sku should = 0." + "description": "Optional. The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0." } }, "subnetResourceId": { @@ -712,10 +712,10 @@ "displayName": { "value": "[parameters('apis')[copyIndex()].displayName]" }, - "apiName": { + "name": { "value": "[parameters('apis')[copyIndex()].name]" }, - "apiPath": { + "path": { "value": "[parameters('apis')[copyIndex()].path]" }, "apiDescription": { @@ -784,14 +784,14 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "8244071930603774060" + "templateHash": "4127687153063244658" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", "owner": "Azure/module-maintainers" }, "parameters": { - "apiName": { + "name": { "type": "string", "metadata": { "description": "Required. API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number." @@ -916,10 +916,10 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. The name of the API management service logger." + "description": "Conditional. The name of the API management service logger. Required if using api/diagnostics." } }, - "apiPath": { + "path": { "type": "string", "metadata": { "description": "Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." @@ -1001,7 +1001,7 @@ "api": { "type": "Microsoft.ApiManagement/service/apis", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('apiName'))]", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "apiRevision": "[parameters('apiRevision')]", "apiRevisionDescription": "[parameters('apiRevisionDescription')]", @@ -1014,7 +1014,7 @@ "displayName": "[parameters('displayName')]", "format": "[if(not(empty(parameters('value'))), parameters('format'), null())]", "isCurrent": "[parameters('isCurrent')]", - "path": "[parameters('apiPath')]", + "path": "[parameters('path')]", "protocols": "[parameters('protocols')]", "serviceUrl": "[parameters('serviceUrl')]", "sourceApiId": "[parameters('sourceApiId')]", @@ -1046,7 +1046,7 @@ "value": "[parameters('apiManagementServiceName')]" }, "apiName": { - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "format": "[if(contains(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), createObject('value', coalesce(parameters('policies'), createArray())[copyIndex()].format), createObject('value', 'xml'))]", "value": { @@ -1160,12 +1160,14 @@ }, "mode": "Incremental", "parameters": { - "diagnosticName": "[if(not(empty(coalesce(parameters('diagnostics'), createArray())[copyIndex()].diagnosticName)), createObject('value', coalesce(parameters('diagnostics'), createArray())[copyIndex()].diagnosticName), createObject('value', format('{0}-diagnostics-{1}', parameters('apiName'), copyIndex())))]", + "name": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'name')]" + }, "apiManagementServiceName": { "value": "[parameters('apiManagementServiceName')]" }, "apiName": { - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "loggerName": { "value": "[parameters('loggerName')]" @@ -1205,7 +1207,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "10754963300235270688" + "templateHash": "15990218139655805007" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -1230,7 +1232,7 @@ "description": "Required. The name of the logger." } }, - "diagnosticName": { + "name": { "type": "string", "defaultValue": "local", "allowedValues": [ @@ -1239,7 +1241,7 @@ "local" ], "metadata": { - "description": "Optional. Type of diagnostic resource. Default is local." + "description": "Optional. Type of diagnostic resource." } }, "alwaysLog": { @@ -1272,21 +1274,21 @@ "W3C" ], "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights." + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." } }, "logClientIp": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Log the ClientIP. Default is false." + "description": "Optional. Log the ClientIP." } }, "metrics": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights." + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." } }, "operationNameFormat": { @@ -1297,14 +1299,14 @@ "URI" ], "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights." + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." } }, "samplingPercentage": { "type": "int", "defaultValue": 100, "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100." + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." } }, "verbosity": { @@ -1316,7 +1318,7 @@ "verbose" ], "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies. Default is \"error\"." + "description": "Optional. The verbosity level applied to traces emitted by trace policies." } } }, @@ -1324,7 +1326,7 @@ { "type": "Microsoft.ApiManagement/service/apis/diagnostics", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", "properties": { "alwaysLog": "[parameters('alwaysLog')]", "backend": "[parameters('backend')]", @@ -1348,14 +1350,14 @@ "metadata": { "description": "The resource ID of the API diagnostic." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" }, "name": { "type": "string", "metadata": { "description": "The name of the API diagnostic." }, - "value": "[parameters('diagnosticName')]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", @@ -1378,14 +1380,14 @@ "metadata": { "description": "The name of the API management service API." }, - "value": "[parameters('apiName')]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { "description": "The resource ID of the API management service API." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('apiName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -2092,18 +2094,38 @@ "value": "[parameters('apiDiagnostics')[copyIndex()].apiName]" }, "loggerName": { - "value": "[parameters('apiDiagnostics')[copyIndex()].loggerName]" - }, - "diagnosticName": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'diagnosticName'), createObject('value', parameters('apiDiagnostics')[copyIndex()].diagnosticName), createObject('value', 'local'))]", - "alwaysLog": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'alwaysLog'), createObject('value', parameters('apiDiagnostics')[copyIndex()].alwaysLog), createObject('value', 'allErrors'))]", - "backend": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'backend'), createObject('value', parameters('apiDiagnostics')[copyIndex()].backend), createObject('value', createObject()))]", - "frontend": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'frontend'), createObject('value', parameters('apiDiagnostics')[copyIndex()].frontend), createObject('value', createObject()))]", - "httpCorrelationProtocol": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'httpCorrelationProtocol'), createObject('value', parameters('apiDiagnostics')[copyIndex()].httpCorrelationProtocol), createObject('value', 'Legacy'))]", - "logClientIp": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'logClientIp'), createObject('value', parameters('apiDiagnostics')[copyIndex()].logClientIp), createObject('value', false()))]", - "metrics": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'metrics'), createObject('value', parameters('apiDiagnostics')[copyIndex()].metrics), createObject('value', false()))]", - "operationNameFormat": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'operationNameFormat'), createObject('value', parameters('apiDiagnostics')[copyIndex()].operationNameFormat), createObject('value', 'Name'))]", - "samplingPercentage": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'samplingPercentage'), createObject('value', parameters('apiDiagnostics')[copyIndex()].samplingPercentage), createObject('value', 100))]", - "verbosity": "[if(contains(parameters('apiDiagnostics')[copyIndex()], 'verbosity'), createObject('value', parameters('apiDiagnostics')[copyIndex()].verbosity), createObject('value', 'error'))]" + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'loggerName')]" + }, + "name": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'name')]" + }, + "alwaysLog": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'alwaysLog')]" + }, + "backend": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'backend')]" + }, + "frontend": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'frontend')]" + }, + "httpCorrelationProtocol": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'httpCorrelationProtocol')]" + }, + "logClientIp": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'logClientIp')]" + }, + "metrics": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'metrics')]" + }, + "operationNameFormat": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'operationNameFormat')]" + }, + "samplingPercentage": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'samplingPercentage')]" + }, + "verbosity": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'verbosity')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2112,7 +2134,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "10754963300235270688" + "templateHash": "15990218139655805007" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -2137,7 +2159,7 @@ "description": "Required. The name of the logger." } }, - "diagnosticName": { + "name": { "type": "string", "defaultValue": "local", "allowedValues": [ @@ -2146,7 +2168,7 @@ "local" ], "metadata": { - "description": "Optional. Type of diagnostic resource. Default is local." + "description": "Optional. Type of diagnostic resource." } }, "alwaysLog": { @@ -2179,21 +2201,21 @@ "W3C" ], "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Default is Legacy. Required if using Application Insights." + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." } }, "logClientIp": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Log the ClientIP. Default is false." + "description": "Optional. Log the ClientIP." } }, "metrics": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Default is false. Required if using Application Insights." + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." } }, "operationNameFormat": { @@ -2204,14 +2226,14 @@ "URI" ], "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Default is Name. Required if using Application Insights." + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." } }, "samplingPercentage": { "type": "int", "defaultValue": 100, "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged. Default is 100." + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." } }, "verbosity": { @@ -2223,7 +2245,7 @@ "verbose" ], "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies. Default is \"error\"." + "description": "Optional. The verbosity level applied to traces emitted by trace policies." } } }, @@ -2231,7 +2253,7 @@ { "type": "Microsoft.ApiManagement/service/apis/diagnostics", "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", "properties": { "alwaysLog": "[parameters('alwaysLog')]", "backend": "[parameters('backend')]", @@ -2255,14 +2277,14 @@ "metadata": { "description": "The resource ID of the API diagnostic." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('diagnosticName'))]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" }, "name": { "type": "string", "metadata": { "description": "The name of the API diagnostic." }, - "value": "[parameters('diagnosticName')]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", @@ -2500,7 +2522,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "11887059084876487326" + "templateHash": "11123719171758533120" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", @@ -2527,8 +2549,9 @@ }, "isBuffered": { "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Whether records are buffered in the logger before publishing. Default is assumed to be true." + "description": "Optional. Whether records are buffered in the logger before publishing." } }, "loggerType": { @@ -2549,7 +2572,7 @@ } }, "credentials": { - "type": "object", + "type": "secureObject", "metadata": { "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger." } 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 6cd23e1e09..1f321c8cd7 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 @@ -150,7 +150,7 @@ module testDeployment '../../../main.bicep' = [ loggerName: 'logger' apiName: 'echo-api' metrics: true - diagnosticName: 'applicationinsights' + name: 'applicationinsights' } ] diagnosticSettings: [ diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md index d4bd78a58a..24315945b5 100644 --- a/avm/res/app/job/README.md +++ b/avm/res/app/job/README.md @@ -943,6 +943,11 @@ The CPU limit of the container in cores. - Required: Yes - Type: string +- Example: + ```Bicep + '0.25' + '1' + ``` ### Parameter: `containers.resources.memory` @@ -950,6 +955,12 @@ The required memory. - Required: Yes - Type: string +- Example: + ```Bicep + '250Mb' + '1.5Gi' + '1500Mi' + ``` ### Parameter: `containers.volumeMounts` @@ -1084,6 +1095,24 @@ Scaling rules for the job. - Required: Yes - Type: array +- Example: + ```Bicep + [ + // for type azure-queue + { + name: 'myrule' + type: 'azure-queue' + metadata: { + queueName: 'default' + storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount' + } + auth: { + secretRef: 'mysecret' + triggerParameter: 'queueName' + } + } + ] + ``` **Required parameters** @@ -1140,6 +1169,16 @@ Metadata properties to describe the scale rule. - Required: Yes - Type: object +- Example: + ```Bicep + { + "// for type azure-queue + { + queueName: 'default' + storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount' + }" + } + ``` ### Parameter: `eventTriggerConfig.scale.rules.type` @@ -1147,6 +1186,12 @@ The type of the rule. - Required: Yes - Type: string +- Example: + ```Bicep + "azure-servicebus" + "azure-queue" + "redis" + ``` ### Parameter: `eventTriggerConfig.parallelism` @@ -1216,6 +1261,11 @@ Cron formatted repeating schedule ("* * * * *") of a Cron Job. It supports the s - Required: Yes - Type: string +- Example: + ```Bicep + '* * * * *' // Every minute, every hour, every day + '0 0 * * *' // at 00:00 UTC every day + ``` ### Parameter: `scheduleTriggerConfig.parallelism` @@ -1302,6 +1352,11 @@ The CPU limit of the container in cores. - Required: Yes - Type: string +- Example: + ```Bicep + '0.25' + '1' + ``` ### Parameter: `initContainers.resources.memory` @@ -1309,6 +1364,12 @@ The required memory. - Required: Yes - Type: string +- Example: + ```Bicep + '250Mb' + '1.5Gi' + '1500Mi' + ``` ### Parameter: `initContainers.args` @@ -1330,6 +1391,19 @@ The environment variables to set in the container. - Required: No - Type: array +- Example: + ```Bicep + [ + { + name: 'AZURE_STORAGE_QUEUE_NAME' + value: '' + } + { + name: 'AZURE_STORAGE_CONNECTION_STRING' + secretRef: 'connection-string' + } + ] + ``` **Required parameters** @@ -1456,6 +1530,18 @@ The managed identity definition for this resource. - Required: No - Type: object +- Example: + ```Bicep + { + systemAssigned: true, + userAssignedResourceIds: [ + '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity' + ] + } + { + systemAssigned: true + } + ``` **Optional parameters** @@ -1484,6 +1570,24 @@ Collection of private container registry credentials for containers used by the - Required: No - Type: array +- Example: + ```Bicep + [ + { + server: 'myregistry.azurecr.io' + identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity' + } + { + server: 'myregistry2.azurecr.io' + identity: 'system' + } + { + server: 'myregistry3.azurecr.io' + username: 'myusername' + passwordSecretRef: 'secret-name' + } + ] + ``` **Required parameters** @@ -1510,6 +1614,7 @@ The FQDN name of the container registry. - Required: Yes - Type: string +- Example: `myregistry.azurecr.io` ### Parameter: `registries.passwordSecretRef` @@ -1524,6 +1629,11 @@ The resource ID of the (user) managed identity, which is used to access the Azur - Required: No - Type: string +- Example: + ```Bicep + user-assigned identity: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity + system-assigned identity: system + ``` ### Parameter: `registries.username` @@ -1643,6 +1753,29 @@ The secrets of the Container App. - Required: No - Type: array +- Example: + ```Bicep + [ + { + name: 'mysecret' + identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity' + keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret' + } + { + name: 'mysecret' + identity: 'system' + keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret' + } + { + name: 'mysecret' + value: 'mysecretvalue' + } + { + name: 'connection-string' + value: listKeys('/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount', '2023-04-01').keys[0].value + } + ] + ``` **Conditional parameters** @@ -1664,6 +1797,7 @@ Azure Key Vault URL pointing to the secret referenced by the Container App Job. - Required: No - Type: string +- Example: `https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret` ### Parameter: `secrets.value` @@ -1692,6 +1826,13 @@ Tags of the resource. - Required: No - Type: object +- Example: + ```Bicep + { + key1: 'value1' + key2: 'value2' + } + ``` ### Parameter: `volumes` diff --git a/avm/res/compute/virtual-machine-scale-set/README.md b/avm/res/compute/virtual-machine-scale-set/README.md index bf45b23c52..a6bb0e788f 100644 --- a/avm/res/compute/virtual-machine-scale-set/README.md +++ b/avm/res/compute/virtual-machine-scale-set/README.md @@ -1624,11 +1624,11 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | | [`maxBatchInstancePercent`](#parameter-maxbatchinstancepercent) | int | The maximum percent of total virtual machine instances that will be upgraded simultaneously by the rolling upgrade in one batch. As this is a maximum, unhealthy instances in previous or future batches can cause the percentage of instances in a batch to decrease to ensure higher reliability. | -| [`maxPriceForLowPriorityVm`](#parameter-maxpriceforlowpriorityvm) | string | Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. | +| [`maxPriceForLowPriorityVm`](#parameter-maxpriceforlowpriorityvm) | int | Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. | | [`maxSurge`](#parameter-maxsurge) | bool | Create new virtual machines to upgrade the scale set, rather than updating the existing virtual machines. Existing virtual machines will be deleted once the new virtual machines are created for each batch. | | [`maxUnhealthyInstancePercent`](#parameter-maxunhealthyinstancepercent) | int | The maximum percentage of the total virtual machine instances in the scale set that can be simultaneously unhealthy, either as a result of being upgraded, or by being found in an unhealthy state by the virtual machine health checks before the rolling upgrade aborts. This constraint will be checked prior to starting any batch. | | [`maxUnhealthyUpgradedInstancePercent`](#parameter-maxunhealthyupgradedinstancepercent) | int | The maximum percentage of the total virtual machine instances in the scale set that can be simultaneously unhealthy, either as a result of being upgraded, or by being found in an unhealthy state by the virtual machine health checks before the rolling upgrade aborts. This constraint will be checked prior to starting any batch. | -| [`monitoringWorkspaceId`](#parameter-monitoringworkspaceid) | string | Resource ID of the monitoring log analytics workspace. | +| [`monitoringWorkspaceResourceId`](#parameter-monitoringworkspaceresourceid) | string | Resource ID of the monitoring log analytics workspace. | | [`orchestrationMode`](#parameter-orchestrationmode) | string | Specifies the orchestration mode for the virtual machine scale set. | | [`overprovision`](#parameter-overprovision) | bool | Specifies whether the Virtual Machine Scale Set should be overprovisioned. | | [`pauseTimeBetweenBatches`](#parameter-pausetimebetweenbatches) | string | The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. | @@ -2195,8 +2195,7 @@ The maximum percent of total virtual machine instances that will be upgraded sim Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. - Required: No -- Type: string -- Default: `''` +- Type: int ### Parameter: `maxSurge` @@ -2222,7 +2221,7 @@ The maximum percentage of the total virtual machine instances in the scale set t - Type: int - Default: `20` -### Parameter: `monitoringWorkspaceId` +### Parameter: `monitoringWorkspaceResourceId` Resource ID of the monitoring log analytics workspace. diff --git a/avm/res/compute/virtual-machine-scale-set/extension/main.json b/avm/res/compute/virtual-machine-scale-set/extension/main.json index 6edd526b76..33c41add0b 100644 --- a/avm/res/compute/virtual-machine-scale-set/extension/main.json +++ b/avm/res/compute/virtual-machine-scale-set/extension/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", diff --git a/avm/res/compute/virtual-machine-scale-set/main.bicep b/avm/res/compute/virtual-machine-scale-set/main.bicep index 1a446e1aff..597ae73d18 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.bicep +++ b/avm/res/compute/virtual-machine-scale-set/main.bicep @@ -70,7 +70,7 @@ param vmPriority string = 'Regular' param enableEvictionPolicy bool = false @description('Optional. Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars.') -param maxPriceForLowPriorityVm string = '' +param maxPriceForLowPriorityVm int? @description('Optional. Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system.') @allowed([ @@ -100,7 +100,7 @@ param extensionMonitoringAgentConfig object = { } @description('Optional. Resource ID of the monitoring log analytics workspace.') -param monitoringWorkspaceId string = '' +param monitoringWorkspaceResourceId string = '' @description('Optional. The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed.') param extensionDependencyAgentConfig object = { @@ -319,14 +319,12 @@ var formattedUserAssignedIdentities = reduce( var identity = !empty(managedIdentities) ? { type: (managedIdentities.?systemAssigned ?? false) - ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } : null -var enableReferencedModulesTelemetry = false - var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') 'Data Operator for Managed Disks': subscriptionResourceId( @@ -393,24 +391,23 @@ var builtInRoleNames = { ) } -resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = - if (enableTelemetry) { - name: '46d3xbcp.res.compute-virtualmachinescaleset.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' - properties: { - mode: 'Incremental' - template: { - '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' - contentVersion: '1.0.0.0' - resources: [] - outputs: { - telemetry: { - type: 'String' - value: 'For more information, see https://aka.ms/avm/TelemetryInfo' - } +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.res.compute-virtualmachinescaleset.${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 vmss 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = { name: name @@ -539,7 +536,7 @@ resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = { licenseType: empty(licenseType) ? null : licenseType priority: vmPriority evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null - billingProfile: !empty(vmPriority) && !empty(maxPriceForLowPriorityVm) + billingProfile: !empty(vmPriority) && null != maxPriceForLowPriorityVm ? { maxPrice: maxPriceForLowPriorityVm } @@ -565,237 +562,225 @@ resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = { plan: !empty(plan) ? plan : null } -module vmss_domainJoinExtension 'extension/main.bicep' = - if (extensionDomainJoinConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-DomainJoin' - params: { - virtualMachineScaleSetName: vmss.name - name: 'DomainJoin' - publisher: 'Microsoft.Compute' - type: 'JsonADDomainExtension' - typeHandlerVersion: contains(extensionDomainJoinConfig, 'typeHandlerVersion') - ? extensionDomainJoinConfig.typeHandlerVersion - : '1.3' - autoUpgradeMinorVersion: contains(extensionDomainJoinConfig, 'autoUpgradeMinorVersion') - ? extensionDomainJoinConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionDomainJoinConfig, 'enableAutomaticUpgrade') - ? extensionDomainJoinConfig.enableAutomaticUpgrade - : false - settings: extensionDomainJoinConfig.settings - protectedSettings: { - Password: extensionDomainJoinPassword - } +module vmss_domainJoinExtension 'extension/main.bicep' = if (extensionDomainJoinConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-DomainJoin' + params: { + virtualMachineScaleSetName: vmss.name + name: 'DomainJoin' + publisher: 'Microsoft.Compute' + type: 'JsonADDomainExtension' + typeHandlerVersion: contains(extensionDomainJoinConfig, 'typeHandlerVersion') + ? extensionDomainJoinConfig.typeHandlerVersion + : '1.3' + autoUpgradeMinorVersion: contains(extensionDomainJoinConfig, 'autoUpgradeMinorVersion') + ? extensionDomainJoinConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionDomainJoinConfig, 'enableAutomaticUpgrade') + ? extensionDomainJoinConfig.enableAutomaticUpgrade + : false + settings: extensionDomainJoinConfig.settings + protectedSettings: { + Password: extensionDomainJoinPassword } } +} -module vmss_microsoftAntiMalwareExtension 'extension/main.bicep' = - if (extensionAntiMalwareConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-MicrosoftAntiMalware' - params: { - virtualMachineScaleSetName: vmss.name - name: 'MicrosoftAntiMalware' - publisher: 'Microsoft.Azure.Security' - type: 'IaaSAntimalware' - typeHandlerVersion: contains(extensionAntiMalwareConfig, 'typeHandlerVersion') - ? extensionAntiMalwareConfig.typeHandlerVersion - : '1.3' - autoUpgradeMinorVersion: contains(extensionAntiMalwareConfig, 'autoUpgradeMinorVersion') - ? extensionAntiMalwareConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionAntiMalwareConfig, 'enableAutomaticUpgrade') - ? extensionAntiMalwareConfig.enableAutomaticUpgrade - : false - settings: extensionAntiMalwareConfig.settings - } - dependsOn: [ - vmss_domainJoinExtension - ] +module vmss_microsoftAntiMalwareExtension 'extension/main.bicep' = if (extensionAntiMalwareConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-MicrosoftAntiMalware' + params: { + virtualMachineScaleSetName: vmss.name + name: 'MicrosoftAntiMalware' + publisher: 'Microsoft.Azure.Security' + type: 'IaaSAntimalware' + typeHandlerVersion: contains(extensionAntiMalwareConfig, 'typeHandlerVersion') + ? extensionAntiMalwareConfig.typeHandlerVersion + : '1.3' + autoUpgradeMinorVersion: contains(extensionAntiMalwareConfig, 'autoUpgradeMinorVersion') + ? extensionAntiMalwareConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionAntiMalwareConfig, 'enableAutomaticUpgrade') + ? extensionAntiMalwareConfig.enableAutomaticUpgrade + : false + settings: extensionAntiMalwareConfig.settings } + dependsOn: [ + vmss_domainJoinExtension + ] +} -resource vmss_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = - if (!empty(monitoringWorkspaceId)) { - name: last(split((!empty(monitoringWorkspaceId) ? monitoringWorkspaceId : 'law'), '/'))! - scope: az.resourceGroup( - split((!empty(monitoringWorkspaceId) ? monitoringWorkspaceId : '//'), '/')[2], - split((!empty(monitoringWorkspaceId) ? monitoringWorkspaceId : '////'), '/')[4] - ) - } +resource vmss_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(monitoringWorkspaceResourceId)) { + name: last(split((!empty(monitoringWorkspaceResourceId) ? monitoringWorkspaceResourceId : 'law'), '/'))! + scope: az.resourceGroup( + split((!empty(monitoringWorkspaceResourceId) ? monitoringWorkspaceResourceId : '//'), '/')[2], + split((!empty(monitoringWorkspaceResourceId) ? monitoringWorkspaceResourceId : '////'), '/')[4] + ) +} -module vmss_azureMonitorAgentExtension 'extension/main.bicep' = - if (extensionMonitoringAgentConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-AzureMonitorAgent' - params: { - virtualMachineScaleSetName: vmss.name - name: 'AzureMonitorAgent' - publisher: 'Microsoft.Azure.Monitor' - type: osType == 'Windows' ? 'AzureMonitorWindowsAgent' : 'AzureMonitorLinuxAgent' - typeHandlerVersion: contains(extensionMonitoringAgentConfig, 'typeHandlerVersion') - ? extensionMonitoringAgentConfig.typeHandlerVersion - : (osType == 'Windows' ? '1.22' : '1.29') - autoUpgradeMinorVersion: contains(extensionMonitoringAgentConfig, 'autoUpgradeMinorVersion') - ? extensionMonitoringAgentConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionMonitoringAgentConfig, 'enableAutomaticUpgrade') - ? extensionMonitoringAgentConfig.enableAutomaticUpgrade - : false - settings: { - workspaceId: !empty(monitoringWorkspaceId) - ? reference(vmss_logAnalyticsWorkspace.id, vmss_logAnalyticsWorkspace.apiVersion).customerId - : '' - GCS_AUTO_CONFIG: osType == 'Linux' ? true : null - } - protectedSettings: { - workspaceKey: !empty(monitoringWorkspaceId) ? vmss_logAnalyticsWorkspace.listKeys().primarySharedKey : '' - } +module vmss_azureMonitorAgentExtension 'extension/main.bicep' = if (extensionMonitoringAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-AzureMonitorAgent' + params: { + virtualMachineScaleSetName: vmss.name + name: 'AzureMonitorAgent' + publisher: 'Microsoft.Azure.Monitor' + type: osType == 'Windows' ? 'AzureMonitorWindowsAgent' : 'AzureMonitorLinuxAgent' + typeHandlerVersion: contains(extensionMonitoringAgentConfig, 'typeHandlerVersion') + ? extensionMonitoringAgentConfig.typeHandlerVersion + : (osType == 'Windows' ? '1.22' : '1.29') + autoUpgradeMinorVersion: contains(extensionMonitoringAgentConfig, 'autoUpgradeMinorVersion') + ? extensionMonitoringAgentConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionMonitoringAgentConfig, 'enableAutomaticUpgrade') + ? extensionMonitoringAgentConfig.enableAutomaticUpgrade + : false + settings: { + workspaceId: !empty(monitoringWorkspaceResourceId) ? vmss_logAnalyticsWorkspace.properties.customerId : '' + GCS_AUTO_CONFIG: osType == 'Linux' ? true : null + } + protectedSettings: { + workspaceKey: !empty(monitoringWorkspaceResourceId) ? vmss_logAnalyticsWorkspace.listKeys().primarySharedKey : '' } - dependsOn: [ - vmss_microsoftAntiMalwareExtension - ] } + dependsOn: [ + vmss_microsoftAntiMalwareExtension + ] +} -module vmss_dependencyAgentExtension 'extension/main.bicep' = - if (extensionDependencyAgentConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-DependencyAgent' - params: { - virtualMachineScaleSetName: vmss.name - name: 'DependencyAgent' - publisher: 'Microsoft.Azure.Monitoring.DependencyAgent' - type: osType == 'Windows' ? 'DependencyAgentWindows' : 'DependencyAgentLinux' - typeHandlerVersion: contains(extensionDependencyAgentConfig, 'typeHandlerVersion') - ? extensionDependencyAgentConfig.typeHandlerVersion - : '9.5' - autoUpgradeMinorVersion: contains(extensionDependencyAgentConfig, 'autoUpgradeMinorVersion') - ? extensionDependencyAgentConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionDependencyAgentConfig, 'enableAutomaticUpgrade') - ? extensionDependencyAgentConfig.enableAutomaticUpgrade - : true - } - dependsOn: [ - vmss_azureMonitorAgentExtension - ] +module vmss_dependencyAgentExtension 'extension/main.bicep' = if (extensionDependencyAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-DependencyAgent' + params: { + virtualMachineScaleSetName: vmss.name + name: 'DependencyAgent' + publisher: 'Microsoft.Azure.Monitoring.DependencyAgent' + type: osType == 'Windows' ? 'DependencyAgentWindows' : 'DependencyAgentLinux' + typeHandlerVersion: contains(extensionDependencyAgentConfig, 'typeHandlerVersion') + ? extensionDependencyAgentConfig.typeHandlerVersion + : '9.5' + autoUpgradeMinorVersion: contains(extensionDependencyAgentConfig, 'autoUpgradeMinorVersion') + ? extensionDependencyAgentConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionDependencyAgentConfig, 'enableAutomaticUpgrade') + ? extensionDependencyAgentConfig.enableAutomaticUpgrade + : true } + dependsOn: [ + vmss_azureMonitorAgentExtension + ] +} -module vmss_networkWatcherAgentExtension 'extension/main.bicep' = - if (extensionNetworkWatcherAgentConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-NetworkWatcherAgent' - params: { - virtualMachineScaleSetName: vmss.name - name: 'NetworkWatcherAgent' - publisher: 'Microsoft.Azure.NetworkWatcher' - type: osType == 'Windows' ? 'NetworkWatcherAgentWindows' : 'NetworkWatcherAgentLinux' - typeHandlerVersion: contains(extensionNetworkWatcherAgentConfig, 'typeHandlerVersion') - ? extensionNetworkWatcherAgentConfig.typeHandlerVersion - : '1.4' - autoUpgradeMinorVersion: contains(extensionNetworkWatcherAgentConfig, 'autoUpgradeMinorVersion') - ? extensionNetworkWatcherAgentConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionNetworkWatcherAgentConfig, 'enableAutomaticUpgrade') - ? extensionNetworkWatcherAgentConfig.enableAutomaticUpgrade - : false - } - dependsOn: [ - vmss_dependencyAgentExtension - ] +module vmss_networkWatcherAgentExtension 'extension/main.bicep' = if (extensionNetworkWatcherAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-NetworkWatcherAgent' + params: { + virtualMachineScaleSetName: vmss.name + name: 'NetworkWatcherAgent' + publisher: 'Microsoft.Azure.NetworkWatcher' + type: osType == 'Windows' ? 'NetworkWatcherAgentWindows' : 'NetworkWatcherAgentLinux' + typeHandlerVersion: contains(extensionNetworkWatcherAgentConfig, 'typeHandlerVersion') + ? extensionNetworkWatcherAgentConfig.typeHandlerVersion + : '1.4' + autoUpgradeMinorVersion: contains(extensionNetworkWatcherAgentConfig, 'autoUpgradeMinorVersion') + ? extensionNetworkWatcherAgentConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionNetworkWatcherAgentConfig, 'enableAutomaticUpgrade') + ? extensionNetworkWatcherAgentConfig.enableAutomaticUpgrade + : false } + dependsOn: [ + vmss_dependencyAgentExtension + ] +} -module vmss_desiredStateConfigurationExtension 'extension/main.bicep' = - if (extensionDSCConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-DesiredStateConfiguration' - params: { - virtualMachineScaleSetName: vmss.name - name: 'DesiredStateConfiguration' - publisher: 'Microsoft.Powershell' - type: 'DSC' - typeHandlerVersion: contains(extensionDSCConfig, 'typeHandlerVersion') - ? extensionDSCConfig.typeHandlerVersion - : '2.77' - autoUpgradeMinorVersion: contains(extensionDSCConfig, 'autoUpgradeMinorVersion') - ? extensionDSCConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionDSCConfig, 'enableAutomaticUpgrade') - ? extensionDSCConfig.enableAutomaticUpgrade - : false - settings: contains(extensionDSCConfig, 'settings') ? extensionDSCConfig.settings : {} - protectedSettings: contains(extensionDSCConfig, 'protectedSettings') ? extensionDSCConfig.protectedSettings : {} - } - dependsOn: [ - vmss_networkWatcherAgentExtension - ] +module vmss_desiredStateConfigurationExtension 'extension/main.bicep' = if (extensionDSCConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-DesiredStateConfiguration' + params: { + virtualMachineScaleSetName: vmss.name + name: 'DesiredStateConfiguration' + publisher: 'Microsoft.Powershell' + type: 'DSC' + typeHandlerVersion: contains(extensionDSCConfig, 'typeHandlerVersion') + ? extensionDSCConfig.typeHandlerVersion + : '2.77' + autoUpgradeMinorVersion: contains(extensionDSCConfig, 'autoUpgradeMinorVersion') + ? extensionDSCConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionDSCConfig, 'enableAutomaticUpgrade') + ? extensionDSCConfig.enableAutomaticUpgrade + : false + settings: contains(extensionDSCConfig, 'settings') ? extensionDSCConfig.settings : {} + protectedSettings: contains(extensionDSCConfig, 'protectedSettings') ? extensionDSCConfig.protectedSettings : {} } + dependsOn: [ + vmss_networkWatcherAgentExtension + ] +} -module vmss_customScriptExtension 'extension/main.bicep' = - if (extensionCustomScriptConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-CustomScriptExtension' - params: { - virtualMachineScaleSetName: vmss.name - name: 'CustomScriptExtension' - publisher: osType == 'Windows' ? 'Microsoft.Compute' : 'Microsoft.Azure.Extensions' - type: osType == 'Windows' ? 'CustomScriptExtension' : 'CustomScript' - typeHandlerVersion: contains(extensionCustomScriptConfig, 'typeHandlerVersion') - ? extensionCustomScriptConfig.typeHandlerVersion - : (osType == 'Windows' ? '1.10' : '2.1') - autoUpgradeMinorVersion: contains(extensionCustomScriptConfig, 'autoUpgradeMinorVersion') - ? extensionCustomScriptConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionCustomScriptConfig, 'enableAutomaticUpgrade') - ? extensionCustomScriptConfig.enableAutomaticUpgrade - : false - settings: { - fileUris: [ - for fileData in extensionCustomScriptConfig.fileData: contains(fileData, 'storageAccountId') - ? '${fileData.uri}?${listAccountSas(fileData.storageAccountId, '2019-04-01', accountSasProperties).accountSasToken}' - : fileData.uri - ] - } - protectedSettings: contains(extensionCustomScriptConfig, 'protectedSettings') - ? extensionCustomScriptConfig.protectedSettings - : {} +module vmss_customScriptExtension 'extension/main.bicep' = if (extensionCustomScriptConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-CustomScriptExtension' + params: { + virtualMachineScaleSetName: vmss.name + name: 'CustomScriptExtension' + publisher: osType == 'Windows' ? 'Microsoft.Compute' : 'Microsoft.Azure.Extensions' + type: osType == 'Windows' ? 'CustomScriptExtension' : 'CustomScript' + typeHandlerVersion: contains(extensionCustomScriptConfig, 'typeHandlerVersion') + ? extensionCustomScriptConfig.typeHandlerVersion + : (osType == 'Windows' ? '1.10' : '2.1') + autoUpgradeMinorVersion: contains(extensionCustomScriptConfig, 'autoUpgradeMinorVersion') + ? extensionCustomScriptConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionCustomScriptConfig, 'enableAutomaticUpgrade') + ? extensionCustomScriptConfig.enableAutomaticUpgrade + : false + settings: { + fileUris: [ + for fileData in extensionCustomScriptConfig.fileData: contains(fileData, 'storageAccountId') + ? '${fileData.uri}?${listAccountSas(fileData.storageAccountId, '2019-04-01', accountSasProperties).accountSasToken}' + : fileData.uri + ] } - dependsOn: [ - vmss_desiredStateConfigurationExtension - ] + protectedSettings: contains(extensionCustomScriptConfig, 'protectedSettings') + ? extensionCustomScriptConfig.protectedSettings + : {} } + dependsOn: [ + vmss_desiredStateConfigurationExtension + ] +} -module vmss_azureDiskEncryptionExtension 'extension/main.bicep' = - if (extensionAzureDiskEncryptionConfig.enabled) { - name: '${uniqueString(deployment().name, location)}-VMSS-AzureDiskEncryption' - params: { - virtualMachineScaleSetName: vmss.name - name: 'AzureDiskEncryption' - publisher: 'Microsoft.Azure.Security' - type: osType == 'Windows' ? 'AzureDiskEncryption' : 'AzureDiskEncryptionForLinux' - typeHandlerVersion: contains(extensionAzureDiskEncryptionConfig, 'typeHandlerVersion') - ? extensionAzureDiskEncryptionConfig.typeHandlerVersion - : (osType == 'Windows' ? '2.2' : '1.1') - autoUpgradeMinorVersion: contains(extensionAzureDiskEncryptionConfig, 'autoUpgradeMinorVersion') - ? extensionAzureDiskEncryptionConfig.autoUpgradeMinorVersion - : true - enableAutomaticUpgrade: contains(extensionAzureDiskEncryptionConfig, 'enableAutomaticUpgrade') - ? extensionAzureDiskEncryptionConfig.enableAutomaticUpgrade - : false - forceUpdateTag: contains(extensionAzureDiskEncryptionConfig, 'forceUpdateTag') - ? extensionAzureDiskEncryptionConfig.forceUpdateTag - : '1.0' - settings: extensionAzureDiskEncryptionConfig.settings - } - dependsOn: [ - vmss_customScriptExtension - ] +module vmss_azureDiskEncryptionExtension 'extension/main.bicep' = if (extensionAzureDiskEncryptionConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-AzureDiskEncryption' + params: { + virtualMachineScaleSetName: vmss.name + name: 'AzureDiskEncryption' + publisher: 'Microsoft.Azure.Security' + type: osType == 'Windows' ? 'AzureDiskEncryption' : 'AzureDiskEncryptionForLinux' + typeHandlerVersion: contains(extensionAzureDiskEncryptionConfig, 'typeHandlerVersion') + ? extensionAzureDiskEncryptionConfig.typeHandlerVersion + : (osType == 'Windows' ? '2.2' : '1.1') + autoUpgradeMinorVersion: contains(extensionAzureDiskEncryptionConfig, 'autoUpgradeMinorVersion') + ? extensionAzureDiskEncryptionConfig.autoUpgradeMinorVersion + : true + enableAutomaticUpgrade: contains(extensionAzureDiskEncryptionConfig, 'enableAutomaticUpgrade') + ? extensionAzureDiskEncryptionConfig.enableAutomaticUpgrade + : false + forceUpdateTag: contains(extensionAzureDiskEncryptionConfig, 'forceUpdateTag') + ? extensionAzureDiskEncryptionConfig.forceUpdateTag + : '1.0' + settings: extensionAzureDiskEncryptionConfig.settings } + dependsOn: [ + vmss_customScriptExtension + ] +} -resource vmss_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: vmss +resource vmss_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: vmss +} resource vmss_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ for (diagnosticSetting, index) in (diagnosticSettings ?? []): { diff --git a/avm/res/compute/virtual-machine-scale-set/main.json b/avm/res/compute/virtual-machine-scale-set/main.json index c1ce67cf03..d8f2e2e1de 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.json +++ b/avm/res/compute/virtual-machine-scale-set/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9133361224761516270" + "version": "0.27.1.19265", + "templateHash": "7041078491413360342" }, "name": "Virtual Machine Scale Sets", "description": "This module deploys a Virtual Machine Scale Set.", @@ -357,8 +357,8 @@ } }, "maxPriceForLowPriorityVm": { - "type": "string", - "defaultValue": "", + "type": "int", + "nullable": true, "metadata": { "description": "Optional. Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars." } @@ -409,7 +409,7 @@ "description": "Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." } }, - "monitoringWorkspaceId": { + "monitoringWorkspaceResourceId": { "type": "string", "defaultValue": "", "metadata": { @@ -808,8 +808,7 @@ "signedProtocol": "https" }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "enableReferencedModulesTelemetry": false, + "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]", @@ -953,7 +952,7 @@ "licenseType": "[if(empty(parameters('licenseType')), null(), parameters('licenseType'))]", "priority": "[parameters('vmPriority')]", "evictionPolicy": "[if(parameters('enableEvictionPolicy'), 'Deallocate', null())]", - "billingProfile": "[if(and(not(empty(parameters('vmPriority'))), not(empty(parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', parameters('maxPriceForLowPriorityVm')), null())]", + "billingProfile": "[if(and(not(empty(parameters('vmPriority'))), not(equals(null(), parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', parameters('maxPriceForLowPriorityVm')), null())]", "scheduledEventsProfile": "[parameters('scheduledEventsProfile')]" }, "overprovision": "[if(equals(parameters('orchestrationMode'), 'Uniform'), parameters('overprovision'), null())]", @@ -973,13 +972,13 @@ "plan": "[if(not(empty(parameters('plan'))), parameters('plan'), null())]" }, "vmss_logAnalyticsWorkspace": { - "condition": "[not(empty(parameters('monitoringWorkspaceId')))]", + "condition": "[not(empty(parameters('monitoringWorkspaceResourceId')))]", "existing": true, "type": "Microsoft.OperationalInsights/workspaces", "apiVersion": "2021-06-01", - "subscriptionId": "[split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), 'law'), '/'))]" + "subscriptionId": "[split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), 'law'), '/'))]" }, "vmss_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", @@ -1090,8 +1089,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1252,8 +1251,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1405,13 +1404,13 @@ "enableAutomaticUpgrade": "[if(contains(parameters('extensionMonitoringAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionMonitoringAgentConfig').enableAutomaticUpgrade), createObject('value', false()))]", "settings": { "value": { - "workspaceId": "[if(not(empty(parameters('monitoringWorkspaceId'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '//'), '/')[2], split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), 'law'), '/'))), '2021-06-01').customerId, '')]", + "workspaceId": "[if(not(empty(parameters('monitoringWorkspaceResourceId'))), reference('vmss_logAnalyticsWorkspace').customerId, '')]", "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" } }, "protectedSettings": { "value": { - "workspaceKey": "[if(not(empty(parameters('monitoringWorkspaceId'))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '//'), '/')[2], split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(parameters('monitoringWorkspaceId'))), parameters('monitoringWorkspaceId'), 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" + "workspaceKey": "[if(not(empty(parameters('monitoringWorkspaceResourceId'))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), '//'), '/')[2], split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" } } }, @@ -1421,8 +1420,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1580,8 +1579,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1738,8 +1737,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1900,8 +1899,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -2068,8 +2067,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -2230,8 +2229,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1737879623418595926" + "version": "0.27.1.19265", + "templateHash": "17283728898456477586" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index d437b5d655..3302521f67 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -24,6 +24,7 @@ This module deploys a Virtual Machine with one or multiple NICs and optionally o | `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | | `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | | `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | | `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | | `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) | @@ -1039,6 +1040,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { backupPolicyName: '' backupVaultName: '' backupVaultResourceGroup: '' + bypassPlatformSafetyChecksOnUserSchedule: true computerName: 'winvm1' dataDisks: [ { @@ -1166,6 +1168,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { kind: 'CanNotDelete' name: 'myCustomLockName' } + maintenanceConfigurationResourceId: '' managedIdentities: { systemAssigned: true userAssignedResourceIds: [ @@ -1334,6 +1337,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "backupVaultResourceGroup": { "value": "" }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "value": true + }, "computerName": { "value": "winvm1" }, @@ -1491,6 +1497,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "name": "myCustomLockName" } }, + "maintenanceConfigurationResourceId": { + "value": "" + }, "managedIdentities": { "value": { "systemAssigned": true, @@ -3137,6 +3146,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`licenseType`](#parameter-licensetype) | string | Specifies that the image or disk that is being used was licensed on-premises. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`maintenanceConfigurationResourceId`](#parameter-maintenanceconfigurationresourceid) | string | The resource Id of a maintenance configuration for this VM. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. The system-assigned managed identity will automatically be enabled if extensionAadJoinConfig.enabled = "True". | | [`maxPriceForLowPriorityVm`](#parameter-maxpriceforlowpriorityvm) | string | Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. | | [`patchAssessmentMode`](#parameter-patchassessmentmode) | string | VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. | @@ -3919,6 +3929,14 @@ Specify the name of lock. - Required: No - Type: string +### Parameter: `maintenanceConfigurationResourceId` + +The resource Id of a maintenance configuration for this VM. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `managedIdentities` The managed identity definition for this resource. The system-assigned managed identity will automatically be enabled if extensionAadJoinConfig.enabled = "True". diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index 11ac66a7c7..ce60427363 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -131,6 +131,9 @@ param backupPolicyName string = 'DefaultPolicy' @description('Optional. The configuration for auto-shutdown.') param autoShutdownConfig object = {} +@description('Optional. The resource Id of a maintenance configuration for this VM.') +param maintenanceConfigurationResourceId string = '' + // Child resources @description('Optional. Specifies whether extension operations should be allowed on the virtual machine. This may only be set to False when no extensions are present on the virtual machine.') param allowExtensionOperations bool = true @@ -607,6 +610,16 @@ resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = { ] } +resource vm_configurationAssignment 'Microsoft.Maintenance/configurationAssignments@2023-04-01' = if (!empty(maintenanceConfigurationResourceId)) { + name: '${vm.name}assignment' + location: location + properties: { + maintenanceConfigurationId: maintenanceConfigurationResourceId + resourceId: vm.id + } + scope: vm +} + resource vm_configurationProfileAssignment 'Microsoft.Automanage/configurationProfileAssignments@2022-05-04' = if (!empty(configurationProfile)) { name: 'default' properties: { diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index d84e3d7372..202bb809dc 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5608164188870152623" + "templateHash": "8007594709688742996" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -567,6 +567,13 @@ "description": "Optional. The configuration for auto-shutdown." } }, + "maintenanceConfigurationResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource Id of a maintenance configuration for this VM." + } + }, "allowExtensionOperations": { "type": "bool", "defaultValue": true, @@ -1041,6 +1048,21 @@ "vm_nic" ] }, + "vm_configurationAssignment": { + "condition": "[not(empty(parameters('maintenanceConfigurationResourceId')))]", + "type": "Microsoft.Maintenance/configurationAssignments", + "apiVersion": "2023-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[format('{0}assignment', parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationResourceId')]", + "resourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" + }, + "dependsOn": [ + "vm" + ] + }, "vm_configurationProfileAssignment": { "condition": "[not(empty(parameters('configurationProfile')))]", "type": "Microsoft.Automanage/configurationProfileAssignments", diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep index 6f1ef21abd..6f2587a5df 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep @@ -1,6 +1,9 @@ @description('Required. The name of the Virtual Network to create.') param virtualNetworkName string +@description('Required. The name of the Maintenance Configuration to create.') +param maintenanceConfigurationName string + @description('Required. The name of the Application Security Group to create.') param applicationSecurityGroupName string @@ -54,6 +57,39 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } } +resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { + name: maintenanceConfigurationName + location: location + properties: { + extensionProperties: { + InGuestPatchMode: 'User' + } + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + startDateTime: '2024-06-16 00:00' + duration: '03:55' + timeZone: 'W. Europe Standard Time' + recurEvery: '1Day' + } + visibility: 'Custom' + installPatches: { + rebootSetting: 'IfRequired' + windowsParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + linuxParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + } + } +} + resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2023-04-01' = { name: applicationSecurityGroupName location: location @@ -275,7 +311,7 @@ resource storageUpload 'Microsoft.Resources/deploymentScripts@2020-10-01' = { properties: { azPowerShellVersion: '9.0' retentionInterval: 'P1D' - arguments: '-StorageAccountName "${storageAccount.name}" -ResourceGroupName "${resourceGroup().name}" -ContainerName "${storageAccount::blobService::container.name}" -FileName "${storageAccountCSEFileName}"' + arguments: '-StorageAccountName ${storageAccount.name} -ResourceGroupName ${resourceGroup().name} -ContainerName ${storageAccount::blobService::container.name} -FileName ${storageAccountCSEFileName}' scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-BlobContent.ps1') } dependsOn: [ @@ -291,6 +327,9 @@ resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@202 @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id +@description('The resource ID of the maintenance configuration.') +output maintenanceConfigurationResourceId string = maintenanceConfiguration.id + @description('The resource ID of the created Application Security Group.') output applicationSecurityGroupResourceId string = applicationSecurityGroup.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep index 60568ad753..bf307d29c7 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep @@ -41,6 +41,7 @@ module nestedDependencies 'dependencies.bicep' = { params: { location: resourceLocation virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' applicationSecurityGroupName: 'dep-${namePrefix}-asg-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' @@ -197,6 +198,8 @@ module testDeployment '../../../main.bicep' = [ ] enableAutomaticUpdates: true patchMode: 'AutomaticByPlatform' + bypassPlatformSafetyChecksOnUserSchedule: true + maintenanceConfigurationResourceId: nestedDependencies.outputs.maintenanceConfigurationResourceId encryptionAtHost: false extensionAntiMalwareConfig: { enabled: true diff --git a/avm/res/databricks/workspace/README.md b/avm/res/databricks/workspace/README.md index 2cf204bb21..e9a01e2beb 100644 --- a/avm/res/databricks/workspace/README.md +++ b/avm/res/databricks/workspace/README.md @@ -1017,6 +1017,8 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`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. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -1218,6 +1220,20 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments` Array of role assignments to create. @@ -1519,7 +1535,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.4.0` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | ## Notes diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index e64e3c8371..949c2ab01b 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -345,12 +345,13 @@ resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 ] @batchSize(1) -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Databricks-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' - privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' @@ -363,7 +364,7 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. } ] : null - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' @@ -458,6 +459,9 @@ type privateEndpointType = { @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('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string @@ -521,6 +525,9 @@ type privateEndpointType = { @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 customerManagedKeyType = { diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index 2b4caf4eca..462d22e223 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "4409228583583673969" + "templateHash": "17360303594498810715" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace.", @@ -57,6 +57,13 @@ "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", "metadata": { @@ -215,6 +222,13 @@ "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." + } } } }, @@ -739,7 +753,7 @@ "name": "[parameters('skuName')]" }, "properties": { - "managedResourceGroupId": "[if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}', subscription().id, parameters('name')))]", + "managedResourceGroupId": "[if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name')))]", "parameters": "[union(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')]", @@ -827,7 +841,8 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Databricks-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -837,8 +852,8 @@ "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Databricks/workspaces', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Databricks/workspaces', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Databricks/workspaces', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Databricks/workspaces', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Databricks/workspaces', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "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.Databricks/workspaces', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Databricks/workspaces', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), 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.Databricks/workspaces', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Databricks/workspaces', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -883,8 +898,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.24.24.22086", - "templateHash": "2592884001616184297" + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1249,7 +1264,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1354,8 +1369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.24.24.22086", - "templateHash": "9321937464667207030" + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md index e381d85ccd..9dff45e745 100644 --- a/avm/res/document-db/database-account/README.md +++ b/avm/res/document-db/database-account/README.md @@ -24,6 +24,8 @@ This module deploys a DocumentDB Database Account. | `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/mongodbDatabases/collections) | | `Microsoft.DocumentDB/databaseAccounts/sqlDatabases` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlDatabases) | | `Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlDatabases/containers) | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleAssignments) | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleDefinitions) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | @@ -47,8 +49,9 @@ The following section provides usage examples for the module, which were used to - [Deploying multiple regions](#example-8-deploying-multiple-regions) - [Plain](#example-9-plain) - [Public network restricted access with ACL](#example-10-public-network-restricted-access-with-acl) -- [SQL Database](#example-11-sql-database) -- [WAF-aligned](#example-12-waf-aligned) +- [Deploying with a sql role definision and assignment](#example-11-deploying-with-a-sql-role-definision-and-assignment) +- [SQL Database](#example-12-sql-database) +- [WAF-aligned](#example-13-waf-aligned) ### Example 1: _Using analytical storage_ @@ -1486,7 +1489,75 @@ module databaseAccount 'br/public:avm/res/document-db/database-account:

-### Example 11: _SQL Database_ +### Example 11: _Deploying with a sql role definision and assignment_ + +This instance deploys the module with sql role definision and assignment + + +

+ +via Bicep module + +```bicep +module databaseAccount 'br/public:avm/res/document-db/database-account:' = { + name: 'databaseAccountDeployment' + params: { + // Required parameters + name: 'role-ref' + // Non-required parameters + location: '' + sqlRoleAssignmentsPrincipalIds: [ + '' + ] + sqlRoleDefinitions: [ + { + name: 'cosmos-sql-role-test' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "role-ref" + }, + // Non-required parameters + "location": { + "value": "" + }, + "sqlRoleAssignmentsPrincipalIds": { + "value": [ + "" + ] + }, + "sqlRoleDefinitions": { + "value": [ + { + "name": "cosmos-sql-role-test" + } + ] + } + } +} +``` + +
+

+ +### Example 12: _SQL Database_ This instance deploys the module with a SQL Database. @@ -2056,7 +2127,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account:

-### Example 12: _WAF-aligned_ +### Example 13: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -2251,6 +2322,8 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: | [`secretsKeyVault`](#parameter-secretskeyvault) | object | Key vault reference and secret settings to add the connection strings and keys generated by the cosmosdb account. | | [`serverVersion`](#parameter-serverversion) | string | Default to 4.2. Specifies the MongoDB server version to use. | | [`sqlDatabases`](#parameter-sqldatabases) | array | SQL Databases configurations. | +| [`sqlRoleAssignmentsPrincipalIds`](#parameter-sqlroleassignmentsprincipalids) | array | SQL Role Definitions configurations. | +| [`sqlRoleDefinitions`](#parameter-sqlroledefinitions) | array | SQL Role Definitions configurations. | | [`tags`](#parameter-tags) | object | Tags of the Database Account resource. | ### Parameter: `name` @@ -3554,6 +3627,70 @@ Default to 400. Request units per second. Will be ignored if autoscaleSettingsMa - Required: No - Type: int +### Parameter: `sqlRoleAssignmentsPrincipalIds` + +SQL Role Definitions configurations. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `sqlRoleDefinitions` + +SQL Role Definitions configurations. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-sqlroledefinitionsname) | string | Name of the SQL Role Definition. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dataAction`](#parameter-sqlroledefinitionsdataaction) | array | An array of data actions that are allowed. | +| [`roleName`](#parameter-sqlroledefinitionsrolename) | string | A user-friendly name for the Role Definition. Must be unique for the database account. | +| [`roleType`](#parameter-sqlroledefinitionsroletype) | string | Indicates whether the Role Definition was built-in or user created. | + +### Parameter: `sqlRoleDefinitions.name` + +Name of the SQL Role Definition. + +- Required: Yes +- Type: string + +### Parameter: `sqlRoleDefinitions.dataAction` + +An array of data actions that are allowed. + +- Required: No +- Type: array + +### Parameter: `sqlRoleDefinitions.roleName` + +A user-friendly name for the Role Definition. Must be unique for the database account. + +- Required: No +- Type: string + +### Parameter: `sqlRoleDefinitions.roleType` + +Indicates whether the Role Definition was built-in or user created. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'BuiltInRole' + 'CustomRole' + ] + ``` + ### Parameter: `tags` Tags of the Database Account resource. @@ -3566,6 +3703,7 @@ Tags of the Database Account resource. | Output | Type | Description | | :-- | :-- | :-- | +| `endpoint` | string | The endpoint of the database account. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the database account. | | `resourceGroupName` | string | The name of the resource group the database account was created in. | diff --git a/avm/res/document-db/database-account/main.bicep b/avm/res/document-db/database-account/main.bicep index 3e8626e9c9..c8c33f24c2 100644 --- a/avm/res/document-db/database-account/main.bicep +++ b/avm/res/document-db/database-account/main.bicep @@ -73,6 +73,12 @@ param serverVersion string = '4.2' @description('Optional. SQL Databases configurations.') param sqlDatabases sqlDatabaseType[] = [] +@description('Optional. SQL Role Definitions configurations.') +param sqlRoleAssignmentsPrincipalIds array = [] + +@description('Optional. SQL Role Definitions configurations.') +param sqlRoleDefinitions sqlRoleDefinitionsType + @description('Optional. MongoDB Databases configurations.') param mongodbDatabases array = [] @@ -408,6 +414,20 @@ module databaseAccount_sqlDatabases 'sql-database/main.bicep' = [ } ] +module databaseAccount_sqlRoleDefinitions 'sql-role/main.bicep' = [ + for sqlRoleDefinition in (sqlRoleDefinitions ?? []): { + name: '${uniqueString(deployment().name, location)}-sqlrd-${sqlRoleDefinition.name}' + params: { + name: sqlRoleDefinition.name + databaseAccountName: databaseAccount.name + dataActions: sqlRoleDefinition.?dataActions + roleName: sqlRoleDefinition.?roleName + roleType: sqlRoleDefinition.?roleType + principalIds: sqlRoleAssignmentsPrincipalIds + } + } +] + module databaseAccount_mongodbDatabases 'mongodb-database/main.bicep' = [ for mongodbDatabase in mongodbDatabases: { name: '${uniqueString(deployment().name, location)}-mongodb-${mongodbDatabase.name}' @@ -546,6 +566,9 @@ output systemAssignedMIPrincipalId string = databaseAccount.?identity.?principal @description('The location the resource was deployed into.') output location string = databaseAccount.location +@description('The endpoint of the database account.') +output endpoint string = databaseAccount.properties.documentEndpoint + // =============== // // Definitions // // =============== // @@ -722,6 +745,20 @@ type failoverLocationsType = { locationName: string } +type sqlRoleDefinitionsType = { + @description('Required. Name of the SQL Role Definition.') + name: string + + @description('Optional. An array of data actions that are allowed.') + dataAction: array? + + @description('Optional. A user-friendly name for the Role Definition. Must be unique for the database account.') + roleName: string? + + @description('Optional. Indicates whether the Role Definition was built-in or user created.') + roleType: ('CustomRole' | 'BuiltInRole')? +}[]? + type sqlDatabaseType = { @description('Required. Name of the SQL database .') name: string diff --git a/avm/res/document-db/database-account/main.json b/avm/res/document-db/database-account/main.json index ec16d7c7a8..bd68978465 100644 --- a/avm/res/document-db/database-account/main.json +++ b/avm/res/document-db/database-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5516885052191994610" + "version": "0.28.1.47646", + "templateHash": "17921062197239369089" }, "name": "DocumentDB Database Accounts", "description": "This module deploys a DocumentDB Database Account.", @@ -467,6 +467,46 @@ } } }, + "sqlRoleDefinitionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Definition." + } + }, + "dataAction": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "allowedValues": [ + "BuiltInRole", + "CustomRole" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + } + }, + "nullable": true + }, "sqlDatabaseType": { "type": "object", "properties": { @@ -907,6 +947,19 @@ "description": "Optional. SQL Databases configurations." } }, + "sqlRoleAssignmentsPrincipalIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SQL Role Definitions configurations." + } + }, + "sqlRoleDefinitions": { + "$ref": "#/definitions/sqlRoleDefinitionsType", + "metadata": { + "description": "Optional. SQL Role Definitions configurations." + } + }, "mongodbDatabases": { "type": "array", "defaultValue": [], @@ -1260,8 +1313,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8319829662998768775" + "version": "0.28.1.47646", + "templateHash": "5335306568168720143" }, "name": "DocumentDB Database Account SQL Databases", "description": "This module deploys a SQL Database in a CosmosDB Account.", @@ -1393,8 +1446,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3824205620211058652" + "version": "0.28.1.47646", + "templateHash": "18224989843504865412" }, "name": "DocumentDB Database Account SQL Database Containers", "description": "This module deploys a SQL Database Container in a CosmosDB Account.", @@ -1611,6 +1664,330 @@ "databaseAccount" ] }, + "databaseAccount_sqlRoleDefinitions": { + "copy": { + "name": "databaseAccount_sqlRoleDefinitions", + "count": "[length(coalesce(parameters('sqlRoleDefinitions'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqlrd-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name]" + }, + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "dataActions": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'dataActions')]" + }, + "roleName": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleName')]" + }, + "roleType": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleType')]" + }, + "principalIds": { + "value": "[parameters('sqlRoleAssignmentsPrincipalIds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "6438933554145929110" + }, + "name": "DocumentDB Database Account SQL Role.", + "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "principalIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Ids needs to be granted." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-definition-{0}', uniqueString(parameters('name')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "dataActions": { + "value": "[parameters('dataActions')]" + }, + "roleName": { + "value": "[parameters('roleName')]" + }, + "roleType": { + "value": "[parameters('roleType')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "5774063578247320117" + }, + "name": "DocumentDB Database Account SQL Role Definitions.", + "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "permissions": [ + { + "dataActions": "[parameters('dataActions')]" + } + ], + "roleName": "[parameters('roleName')]", + "type": "[parameters('roleType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL database." + }, + "value": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + } + }, + { + "copy": { + "name": "sqlRoleAssignment", + "count": "[length(parameters('principalIds'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-assign-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[guid(reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value, parameters('principalIds')[copyIndex()], resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))]" + }, + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value]" + }, + "principalId": { + "value": "[parameters('principalIds')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "4968352550056104037" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Required. Id needs to be granted." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Id of the SQL Role Definition." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Assignment was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name'))))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Definition and Assignment were created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, "databaseAccount_mongodbDatabases": { "copy": { "name": "databaseAccount_mongodbDatabases", @@ -1646,8 +2023,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1527748615955553712" + "version": "0.28.1.47646", + "templateHash": "10789567092566068185" }, "name": "DocumentDB Database Account MongoDB Databases", "description": "This module deploys a MongoDB Database within a CosmosDB Account.", @@ -1749,8 +2126,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6235322895830297683" + "version": "0.28.1.47646", + "templateHash": "15131365585122011750" }, "name": "DocumentDB Database Account MongoDB Database Collections", "description": "This module deploys a MongoDB Database Collection.", @@ -1905,8 +2282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6889435067791947905" + "version": "0.28.1.47646", + "templateHash": "6730968766448186151" }, "name": "DocumentDB Database Account Gremlin Databases", "description": "This module deploys a Gremlin Database within a CosmosDB Account.", @@ -2009,8 +2386,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3987394297554402770" + "version": "0.28.1.47646", + "templateHash": "16910156821054362754" }, "name": "DocumentDB Database Accounts Gremlin Databases Graphs", "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph.", @@ -2872,8 +3249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17423703517558214368" + "version": "0.28.1.47646", + "templateHash": "13154453205210713878" } }, "definitions": { @@ -2965,6 +3342,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('databaseAccount', '2023-04-15', 'full').location]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The endpoint of the database account." + }, + "value": "[reference('databaseAccount').documentEndpoint]" } } } \ No newline at end of file diff --git a/avm/res/document-db/database-account/sql-role/README.md b/avm/res/document-db/database-account/sql-role/README.md new file mode 100644 index 0000000000..71fc8b9386 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/README.md @@ -0,0 +1,116 @@ +# DocumentDB Database Account SQL Role. `[Microsoft.DocumentDB/databaseAccounts]` + +This module deploys SQL Role Definision and Assignment in a CosmosDB Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleAssignments) | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the SQL Role. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseAccountName`](#parameter-databaseaccountname) | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dataActions`](#parameter-dataactions) | array | An array of data actions that are allowed. | +| [`principalIds`](#parameter-principalids) | array | Ids needs to be granted. | +| [`roleName`](#parameter-rolename) | string | A user-friendly name for the Role Definition. Must be unique for the database account. | +| [`roleType`](#parameter-roletype) | string | Indicates whether the Role Definition was built-in or user created. | + +### Parameter: `name` + +Name of the SQL Role. + +- Required: Yes +- Type: string + +### Parameter: `databaseAccountName` + +The name of the parent Database Account. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `dataActions` + +An array of data actions that are allowed. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + ] + ``` + +### Parameter: `principalIds` + +Ids needs to be granted. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleName` + +A user-friendly name for the Role Definition. Must be unique for the database account. + +- Required: No +- Type: string +- Default: `'Reader Writer'` + +### Parameter: `roleType` + +Indicates whether the Role Definition was built-in or user created. + +- Required: No +- Type: string +- Default: `'CustomRole'` +- Allowed: + ```Bicep + [ + 'BuiltInRole' + 'CustomRole' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `resourceGroupName` | string | The name of the resource group the SQL Role Definition and Assignment were created in. | + +## Cross-referenced modules + +_None_ + +## 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/document-db/database-account/sql-role/main.bicep b/avm/res/document-db/database-account/sql-role/main.bicep new file mode 100644 index 0000000000..3de96361c0 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/main.bicep @@ -0,0 +1,66 @@ +metadata name = 'DocumentDB Database Account SQL Role.' +metadata description = 'This module deploys SQL Role Definision and Assignment in a CosmosDB Account.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Required. Name of the SQL Role.') +param name string + +@description('Optional. An array of data actions that are allowed.') +param dataActions array = [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' +] + +@description('Optional. Ids needs to be granted.') +param principalIds array = [] + +@description('Optional. A user-friendly name for the Role Definition. Must be unique for the database account.') +param roleName string = 'Reader Writer' + +@description('Optional. Indicates whether the Role Definition was built-in or user created.') +@allowed([ + 'CustomRole' + 'BuiltInRole' +]) +param roleType string = 'CustomRole' + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = { + name: databaseAccountName +} + +module sqlRoleDefinition 'sql-role-definitions/main.bicep' = { + name: 'sql-role-definition-${uniqueString(name)}' + params: { + databaseAccountName: databaseAccount.name + dataActions: dataActions + roleName: roleName + roleType: roleType + } + dependsOn: [ + databaseAccount + ] +} + +@batchSize(1) +module sqlRoleAssignment 'sql-role-assignments/main.bicep' = [ + for principalId in principalIds: if (!empty(principalId)) { + name: 'sql-role-assign-${uniqueString(principalId)}' + params: { + name: guid(sqlRoleDefinition.outputs.resourceId, principalId, databaseAccount.id) + databaseAccountName: databaseAccountName + roleDefinitionId: sqlRoleDefinition.outputs.resourceId + principalId: principalId + } + dependsOn: [ + databaseAccount + sqlRoleDefinition + ] + } +] + +@description('The name of the resource group the SQL Role Definition and Assignment were created in.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/document-db/database-account/sql-role/main.json b/avm/res/document-db/database-account/sql-role/main.json new file mode 100644 index 0000000000..0d05737081 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/main.json @@ -0,0 +1,286 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "6438933554145929110" + }, + "name": "DocumentDB Database Account SQL Role.", + "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "principalIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Ids needs to be granted." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-definition-{0}', uniqueString(parameters('name')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "dataActions": { + "value": "[parameters('dataActions')]" + }, + "roleName": { + "value": "[parameters('roleName')]" + }, + "roleType": { + "value": "[parameters('roleType')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "5774063578247320117" + }, + "name": "DocumentDB Database Account SQL Role Definitions.", + "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "permissions": [ + { + "dataActions": "[parameters('dataActions')]" + } + ], + "roleName": "[parameters('roleName')]", + "type": "[parameters('roleType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL database." + }, + "value": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + } + }, + { + "copy": { + "name": "sqlRoleAssignment", + "count": "[length(parameters('principalIds'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-assign-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[guid(reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value, parameters('principalIds')[copyIndex()], resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))]" + }, + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value]" + }, + "principalId": { + "value": "[parameters('principalIds')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "4968352550056104037" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Required. Id needs to be granted." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Id of the SQL Role Definition." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Assignment was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name'))))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Definition and Assignment were created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/document-db/database-account/sql-role/sql-role-assignments/README.md b/avm/res/document-db/database-account/sql-role/sql-role-assignments/README.md new file mode 100644 index 0000000000..8dba3341cc --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-assignments/README.md @@ -0,0 +1,77 @@ +# DocumentDB Database Account SQL Role Assignments. `[Microsoft.DocumentDB/databaseaccount/sqlrole/sqlroleassignments]` + +This module deploys a SQL Role Assignment in a CosmosDB Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleAssignments) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the SQL Role Assignment. | +| [`principalId`](#parameter-principalid) | string | Id needs to be granted. | +| [`roleDefinitionId`](#parameter-roledefinitionid) | string | Id of the SQL Role Definition. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseAccountName`](#parameter-databaseaccountname) | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | + +### Parameter: `name` + +Name of the SQL Role Assignment. + +- Required: Yes +- Type: string + +### Parameter: `principalId` + +Id needs to be granted. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `roleDefinitionId` + +Id of the SQL Role Definition. + +- Required: Yes +- Type: string + +### Parameter: `databaseAccountName` + +The name of the parent Database Account. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `resourceGroupName` | string | The name of the resource group the SQL Role Assignment was created in. | + +## Cross-referenced modules + +_None_ + +## 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/document-db/database-account/sql-role/sql-role-assignments/main.bicep b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.bicep new file mode 100644 index 0000000000..32c24742a9 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.bicep @@ -0,0 +1,32 @@ +metadata name = 'DocumentDB Database Account SQL Role Assignments.' +metadata description = 'This module deploys a SQL Role Assignment in a CosmosDB Account.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Required. Name of the SQL Role Assignment.') +param name string + +@description('Required. Id needs to be granted.') +param principalId string = '' + +@description('Required. Id of the SQL Role Definition.') +param roleDefinitionId string + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = { + name: databaseAccountName +} + +resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = { + parent: databaseAccount + name: name + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: databaseAccount.id + } +} + +@description('The name of the resource group the SQL Role Assignment was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json new file mode 100644 index 0000000000..97c74bb0ed --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "12924779272926798782" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Required. Id needs to be granted." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Id of the SQL Role Definition." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Assignment was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/document-db/database-account/sql-role/sql-role-definitions/README.md b/avm/res/document-db/database-account/sql-role/sql-role-definitions/README.md new file mode 100644 index 0000000000..3a1eb3680c --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-definitions/README.md @@ -0,0 +1,88 @@ +# DocumentDB Database Account SQL Role Definitions. `[Microsoft.DocumentDB/databaseaccount/sqlrole/sqlroledefinitions]` + +This module deploys a SQL Role Definision in a CosmosDB Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleDefinitions) | + +## Parameters + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseAccountName`](#parameter-databaseaccountname) | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dataActions`](#parameter-dataactions) | array | An array of data actions that are allowed. | +| [`roleName`](#parameter-rolename) | string | A user-friendly name for the Role Definition. Must be unique for the database account. | +| [`roleType`](#parameter-roletype) | string | Indicates whether the Role Definition was built-in or user created. | + +### Parameter: `databaseAccountName` + +The name of the parent Database Account. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `dataActions` + +An array of data actions that are allowed. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleName` + +A user-friendly name for the Role Definition. Must be unique for the database account. + +- Required: No +- Type: string +- Default: `'Reader Writer'` + +### Parameter: `roleType` + +Indicates whether the Role Definition was built-in or user created. + +- Required: No +- Type: string +- Default: `'CustomRole'` +- Allowed: + ```Bicep + [ + 'BuiltInRole' + 'CustomRole' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SQL database. | +| `resourceGroupName` | string | The name of the resource group the SQL database was created in. | +| `resourceId` | string | The resource ID of the SQL database. | + +## Cross-referenced modules + +_None_ + +## 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/document-db/database-account/sql-role/sql-role-definitions/main.bicep b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.bicep new file mode 100644 index 0000000000..98d8ab4879 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.bicep @@ -0,0 +1,49 @@ +metadata name = 'DocumentDB Database Account SQL Role Definitions.' +metadata description = 'This module deploys a SQL Role Definision in a CosmosDB Account.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Optional. An array of data actions that are allowed.') +param dataActions array = [] + +@description('Optional. A user-friendly name for the Role Definition. Must be unique for the database account.') +param roleName string = 'Reader Writer' + +@description('Optional. Indicates whether the Role Definition was built-in or user created.') +@allowed([ + 'CustomRole' + 'BuiltInRole' +]) +param roleType string = 'CustomRole' + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = { + name: databaseAccountName +} + +resource sqlRoleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2023-04-15' = { + parent: databaseAccount + name: guid(databaseAccount.id, databaseAccountName, 'sql-role') + properties: { + assignableScopes: [ + databaseAccount.id + ] + permissions: [ + { + dataActions: dataActions + } + ] + roleName: roleName + type: roleType + } +} + +@description('The name of the SQL database.') +output name string = sqlRoleDefinition.name + +@description('The resource ID of the SQL database.') +output resourceId string = sqlRoleDefinition.id + +@description('The name of the resource group the SQL database was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json new file mode 100644 index 0000000000..b7029bd4a1 --- /dev/null +++ b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "5774063578247320117" + }, + "name": "DocumentDB Database Account SQL Role Definitions.", + "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "permissions": [ + { + "dataActions": "[parameters('dataActions')]" + } + ], + "roleName": "[parameters('roleName')]", + "type": "[parameters('roleType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL database." + }, + "value": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/document-db/database-account/tests/e2e/role/dependencies.bicep b/avm/res/document-db/database-account/tests/e2e/role/dependencies.bicep new file mode 100644 index 0000000000..b5c4657c14 --- /dev/null +++ b/avm/res/document-db/database-account/tests/e2e/role/dependencies.bicep @@ -0,0 +1,36 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the App Service to create.') +param appName string + +@description('Required. The name of the App Service Plan to create.') +param appServicePlanName string + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: appServicePlanName + location: location + sku: { + name: 'B3' + } + properties: { + reserved: true + } +} + +resource app 'Microsoft.Web/sites@2022-09-01' = { + name: appName + location: location + kind: 'app,linux' + properties: { + serverFarmId: appServicePlan.id + siteConfig: { + linuxFxVersion: 'dotnetcore|8.0' + alwaysOn: true + } + } + identity: { type: 'SystemAssigned' } +} + +@description('The app identity Principal Id.') +output identityPrincipalId string = app.identity.principalId diff --git a/avm/res/document-db/database-account/tests/e2e/role/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/role/main.test.bicep new file mode 100644 index 0000000000..737b310b7c --- /dev/null +++ b/avm/res/document-db/database-account/tests/e2e/role/main.test.bicep @@ -0,0 +1,59 @@ +targetScope = 'subscription' + +metadata name = 'Deploying with a sql role definision and assignment' +metadata description = 'This instance deploys the module with sql role definision and assignment' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-documentdb.databaseaccounts-${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 = 'dddarole' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// Pipeline is selecting random regions which dont support all cosmos features and have constraints when creating new cosmos +#disable-next-line no-hardcoded-location +var enforcedLocation = 'eastus2' + +// ============== // +// General resources +// ============== // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' + params: { + appName: 'dep-${namePrefix}-app-${serviceShort}' + appServicePlanName: 'dep-${namePrefix}-asp-${serviceShort}' + location: enforcedLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-role-${serviceShort}' + params: { + location: enforcedLocation + name: '${namePrefix}-role-ref' + sqlRoleAssignmentsPrincipalIds: [ + nestedDependencies.outputs.identityPrincipalId + ] + sqlRoleDefinitions: [ + { name: 'cosmos-sql-role-test' } + ] + } +} diff --git a/avm/res/event-hub/namespace/README.md b/avm/res/event-hub/namespace/README.md index c8e4813f6b..511d9a0980 100644 --- a/avm/res/event-hub/namespace/README.md +++ b/avm/res/event-hub/namespace/README.md @@ -1355,6 +1355,8 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`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. | | [`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. | @@ -1550,6 +1552,20 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments` Array of role assignments to create. @@ -1824,7 +1840,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.4.0` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-hub/namespace/main.bicep b/avm/res/event-hub/namespace/main.bicep index 8e3df44a89..b9b601e39d 100644 --- a/avm/res/event-hub/namespace/main.bicep +++ b/avm/res/event-hub/namespace/main.bicep @@ -338,12 +338,13 @@ module eventHubNamespace_networkRuleSet 'network-rule-set/main.bicep' = if (!emp } } -module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [ +module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-EventHubNamespace-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-eventHubNamespace-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(eventHubNamespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' - privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(eventHubNamespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -356,7 +357,7 @@ module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-end } ] : null - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(eventHubNamespace.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -520,6 +521,9 @@ type privateEndpointType = { @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? @@ -583,6 +587,9 @@ type privateEndpointType = { @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 = { diff --git a/avm/res/event-hub/namespace/main.json b/avm/res/event-hub/namespace/main.json index bf75aa20c5..6e62b52bc2 100644 --- a/avm/res/event-hub/namespace/main.json +++ b/avm/res/event-hub/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "15950832845960149615" + "templateHash": "10058273912362518894" }, "name": "Event Hub Namespaces", "description": "This module deploys an Event Hub Namespace.", @@ -146,6 +146,13 @@ "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, @@ -305,6 +312,13 @@ "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." + } } } }, @@ -1845,7 +1859,8 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-EventHubNamespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-eventHubNamespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1855,8 +1870,8 @@ "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.EventHub/namespaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace'), copyIndex()))]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.EventHub/namespaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.EventHub/namespaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.EventHub/namespaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.EventHub/namespaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "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.EventHub/namespaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.EventHub/namespaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace')))))), 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.EventHub/namespaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.EventHub/namespaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'namespace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, @@ -1901,8 +1916,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.24.24.22086", - "templateHash": "2592884001616184297" + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -2267,7 +2282,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2372,8 +2387,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.24.24.22086", - "templateHash": "9321937464667207030" + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", diff --git a/avm/res/hybrid-compute/machine/README.md b/avm/res/hybrid-compute/machine/README.md new file mode 100644 index 0000000000..8d2d8eb387 --- /dev/null +++ b/avm/res/hybrid-compute/machine/README.md @@ -0,0 +1,629 @@ +# Hybrid Compute Machines `[Microsoft.HybridCompute/machines]` + +This module deploys an Arc Machine for use with Arc Resource Bridge for Azure Stack HCI or VMware. In these scenarios, this resource module will be used in combination with another resource module to create the require Virtual Machine Instance extension resource on this Arc Machine resource. This module should not be used for other Arc-enabled server scenarios, where the Arc Machine resource is created automatically by the onboarding process. + +## 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.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | +| `Microsoft.HybridCompute/machines` | [2024-03-31-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/machines) | + +## 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/machine:`. + +- [Creates an Arc Machine using only the defaults](#example-1-creates-an-arc-machine-using-only-the-defaults) +- [Creates an Arc Machine with maximum configurations](#example-2-creates-an-arc-machine-with-maximum-configurations) +- [Creates an VMWare machine using only the defaults](#example-3-creates-an-vmware-machine-using-only-the-defaults) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Creates an Arc Machine using only the defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module machine 'br/public:avm/res/hybrid-compute/machine:' = { + name: 'machineDeployment' + params: { + // Required parameters + kind: 'HCI' + name: 'arcmachcimin' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

+ +### Example 2: _Creates an Arc Machine with maximum configurations_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module machine 'br/public:avm/res/hybrid-compute/machine:' = { + name: 'machineDeployment' + params: { + // Required parameters + kind: 'HCI' + name: 'arcmachcimx' + // Non-required parameters + guestConfiguration: { + assignmentType: 'ApplyAndMonitor' + configurationParameter: [ + { + name: 'Minimum Password Length;ExpectedValue' + value: '16' + } + { + name: 'Minimum Password Length;RemediateValue' + value: '16' + } + { + name: 'Maximum Password Age;ExpectedValue' + value: '75' + } + { + name: 'Maximum Password Age;RemediateValue' + value: '75' + } + ] + name: 'AzureWindowsBaseline' + version: '1.*' + } + location: '' + osType: 'Windows' + patchAssessmentMode: 'AutomaticByPlatform' + patchMode: 'AutomaticByPlatform' + privateLinkScopeResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "HCI" + }, + "name": { + "value": "arcmachcimx" + }, + // Non-required parameters + "guestConfiguration": { + "value": { + "assignmentType": "ApplyAndMonitor", + "configurationParameter": [ + { + "name": "Minimum Password Length;ExpectedValue", + "value": "16" + }, + { + "name": "Minimum Password Length;RemediateValue", + "value": "16" + }, + { + "name": "Maximum Password Age;ExpectedValue", + "value": "75" + }, + { + "name": "Maximum Password Age;RemediateValue", + "value": "75" + } + ], + "name": "AzureWindowsBaseline", + "version": "1.*" + } + }, + "location": { + "value": "" + }, + "osType": { + "value": "Windows" + }, + "patchAssessmentMode": { + "value": "AutomaticByPlatform" + }, + "patchMode": { + "value": "AutomaticByPlatform" + }, + "privateLinkScopeResourceId": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +### Example 3: _Creates an VMWare machine using only the defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module machine 'br/public:avm/res/hybrid-compute/machine:' = { + name: 'machineDeployment' + params: { + // Required parameters + kind: 'VMware' + name: 'arcmacvmwmin' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

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

+ +via Bicep module + +```bicep +module machine 'br/public:avm/res/hybrid-compute/machine:' = { + name: 'machineDeployment' + params: { + // Required parameters + kind: 'HCI' + name: 'arcmacwaf' + // Non-required parameters + location: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "HCI" + }, + "name": { + "value": "arcmacwaf" + }, + // Non-required parameters + "location": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-kind) | string | Kind of Arc machine to be created. Possible values are: HCI, SCVMM, VMware. | +| [`name`](#parameter-name) | string | The name of the Arc machine to be created. You should use a unique prefix to reduce name collisions in Active Directory. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`osType`](#parameter-ostype) | string | Required if you are providing OS-type specified configurations, such as patch settings. The chosen OS type, either Windows or Linux. | +| [`privateLinkScopeResourceId`](#parameter-privatelinkscoperesourceid) | string | The resource ID of an Arc Private Link Scope which which to associate this machine. Required if you are using Private Link for Arc and your Arc Machine will resolve a Private Endpoint for connectivity to Azure. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`clientPublicKey`](#parameter-clientpublickey) | securestring | The Public Key that the client provides to be used during initial resource onboarding. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`guestConfiguration`](#parameter-guestconfiguration) | object | The guest configuration for the Arc machine. Needs the Guest Configuration extension to be enabled. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`parentClusterResourceId`](#parameter-parentclusterresourceid) | string | Parent cluster resource ID (Azure Stack HCI). | +| [`patchAssessmentMode`](#parameter-patchassessmentmode) | string | VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. | +| [`patchMode`](#parameter-patchmode) | string | VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`vmId`](#parameter-vmid) | string | The GUID of the on-premises virtual machine from your hypervisor. | + +### Parameter: `kind` + +Kind of Arc machine to be created. Possible values are: HCI, SCVMM, VMware. + +- Required: Yes +- Type: string + +### Parameter: `name` + +The name of the Arc machine to be created. You should use a unique prefix to reduce name collisions in Active Directory. + +- Required: Yes +- Type: string + +### Parameter: `osType` + +Required if you are providing OS-type specified configurations, such as patch settings. The chosen OS type, either Windows or Linux. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Linux' + 'Windows' + ] + ``` + +### Parameter: `privateLinkScopeResourceId` + +The resource ID of an Arc Private Link Scope which which to associate this machine. Required if you are using Private Link for Arc and your Arc Machine will resolve a Private Endpoint for connectivity to Azure. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `clientPublicKey` + +The Public Key that the client provides to be used during initial resource onboarding. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `guestConfiguration` + +The guest configuration for the Arc machine. Needs the Guest Configuration extension to be enabled. + +- Required: No +- Type: object +- Default: `{}` + +### 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: `parentClusterResourceId` + +Parent cluster resource ID (Azure Stack HCI). + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `patchAssessmentMode` + +VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. + +- Required: No +- Type: string +- Default: `'ImageDefault'` +- Allowed: + ```Bicep + [ + 'AutomaticByPlatform' + 'ImageDefault' + ] + ``` + +### Parameter: `patchMode` + +VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AutomaticByOS' + 'AutomaticByPlatform' + 'ImageDefault' + 'Manual' + ] + ``` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**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. | +| [`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.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: `vmId` + +The GUID of the on-premises virtual machine from your hypervisor. + +- Required: No +- Type: string +- Default: `''` + + +## 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. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## 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/machine/extension/README.md b/avm/res/hybrid-compute/machine/extension/README.md new file mode 100644 index 0000000000..aef5344c17 --- /dev/null +++ b/avm/res/hybrid-compute/machine/extension/README.md @@ -0,0 +1,147 @@ +# Arc Machine Extensions `[Microsoft.HybridCompute/machines/extensions]` + +This module deploys a Arc Machine Extension. This module should be used as a standalone deployment after the Arc agent has connected to the Arc Machine resource. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.HybridCompute/machines/extensions` | [2024-03-31-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.HybridCompute/machines/extensions) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`arcMachineName`](#parameter-arcmachinename) | string | The name of the parent Arc Machine that extension is provisioned for. | +| [`autoUpgradeMinorVersion`](#parameter-autoupgrademinorversion) | bool | Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true. | +| [`enableAutomaticUpgrade`](#parameter-enableautomaticupgrade) | bool | Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available. | +| [`name`](#parameter-name) | string | The name of the Arc Machine extension. | +| [`publisher`](#parameter-publisher) | string | The name of the extension handler publisher. | +| [`type`](#parameter-type) | string | Specifies the type of the extension; an example is "CustomScriptExtension". | +| [`typeHandlerVersion`](#parameter-typehandlerversion) | string | Specifies the version of the script handler. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`forceUpdateTag`](#parameter-forceupdatetag) | string | How the extension handler should be forced to update even if the extension configuration has not changed. | +| [`location`](#parameter-location) | string | The location the extension is deployed to. | +| [`protectedSettings`](#parameter-protectedsettings) | secureObject | Any object that contains the extension specific protected settings. | +| [`settings`](#parameter-settings) | object | Any object that contains the extension specific settings. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `arcMachineName` + +The name of the parent Arc Machine that extension is provisioned for. + +- Required: Yes +- Type: string + +### Parameter: `autoUpgradeMinorVersion` + +Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true. + +- Required: Yes +- Type: bool + +### Parameter: `enableAutomaticUpgrade` + +Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available. + +- Required: Yes +- Type: bool + +### Parameter: `name` + +The name of the Arc Machine extension. + +- Required: Yes +- Type: string + +### Parameter: `publisher` + +The name of the extension handler publisher. + +- Required: Yes +- Type: string + +### Parameter: `type` + +Specifies the type of the extension; an example is "CustomScriptExtension". + +- Required: Yes +- Type: string + +### Parameter: `typeHandlerVersion` + +Specifies the version of the script handler. + +- Required: Yes +- Type: string + +### Parameter: `forceUpdateTag` + +How the extension handler should be forced to update even if the extension configuration has not changed. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `location` + +The location the extension is deployed to. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `protectedSettings` + +Any object that contains the extension specific protected settings. + +- Required: No +- Type: secureObject +- Default: `{}` + +### Parameter: `settings` + +Any object that contains the extension specific settings. + +- Required: No +- Type: object +- Default: `{}` + +### 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 extension. | +| `resourceGroupName` | string | The name of the Resource Group the extension was created in. | +| `resourceId` | string | The resource ID of the extension. | + +## Cross-referenced modules + +_None_ + +## 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/machine/extension/main.bicep b/avm/res/hybrid-compute/machine/extension/main.bicep new file mode 100644 index 0000000000..2c68e47c59 --- /dev/null +++ b/avm/res/hybrid-compute/machine/extension/main.bicep @@ -0,0 +1,73 @@ +metadata name = 'Arc Machine Extensions' +metadata description = 'This module deploys a Arc Machine Extension. This module should be used as a standalone deployment after the Arc agent has connected to the Arc Machine resource.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the parent Arc Machine that extension is provisioned for.') +param arcMachineName string + +@description('Required. The name of the Arc Machine extension.') +param name string + +@description('Optional. The location the extension is deployed to.') +param location string = resourceGroup().location + +@description('Required. The name of the extension handler publisher.') +param publisher string + +@description('Required. Specifies the type of the extension; an example is "CustomScriptExtension".') +param type string + +@description('Required. Specifies the version of the script handler.') +param typeHandlerVersion string + +@description('Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true.') +param autoUpgradeMinorVersion bool + +@description('Optional. How the extension handler should be forced to update even if the extension configuration has not changed.') +param forceUpdateTag string = '' + +@description('Optional. Any object that contains the extension specific settings.') +param settings object = {} + +@description('Optional. Any object that contains the extension specific protected settings.') +@secure() +param protectedSettings object = {} + +@description('Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available.') +param enableAutomaticUpgrade bool + +@description('Optional. Tags of the resource.') +param tags object? + +resource machine 'Microsoft.HybridCompute/machines@2024-03-31-preview' existing = { + name: arcMachineName +} + +resource extension 'Microsoft.HybridCompute/machines/extensions@2024-03-31-preview' = { + name: name + parent: machine + location: location + tags: tags + properties: { + publisher: publisher + type: type + typeHandlerVersion: typeHandlerVersion + autoUpgradeMinorVersion: autoUpgradeMinorVersion + enableAutomaticUpgrade: enableAutomaticUpgrade + forceUpdateTag: !empty(forceUpdateTag) ? forceUpdateTag : null + settings: !empty(settings) ? settings : null + protectedSettings: !empty(protectedSettings) ? protectedSettings : null + } +} + +@description('The name of the extension.') +output name string = extension.name + +@description('The resource ID of the extension.') +output resourceId string = extension.id + +@description('The name of the Resource Group the extension was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = extension.location diff --git a/avm/res/hybrid-compute/machine/extension/main.json b/avm/res/hybrid-compute/machine/extension/main.json new file mode 100644 index 0000000000..4c7f7f20b5 --- /dev/null +++ b/avm/res/hybrid-compute/machine/extension/main.json @@ -0,0 +1,152 @@ +{ + "$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.28.1.47646", + "templateHash": "16938870032761436213" + }, + "name": "Arc Machine Extensions", + "description": "This module deploys a Arc Machine Extension. This module should be used as a standalone deployment after the Arc agent has connected to the Arc Machine resource.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "arcMachineName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent Arc Machine that extension is provisioned for." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Arc Machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "machine": { + "existing": true, + "type": "Microsoft.HybridCompute/machines", + "apiVersion": "2024-03-31-preview", + "name": "[parameters('arcMachineName')]" + }, + "extension": { + "type": "Microsoft.HybridCompute/machines/extensions", + "apiVersion": "2024-03-31-preview", + "name": "[format('{0}/{1}', parameters('arcMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]" + }, + "dependsOn": [ + "machine" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.HybridCompute/machines/extensions', parameters('arcMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2024-03-31-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/hybrid-compute/machine/main.bicep b/avm/res/hybrid-compute/machine/main.bicep new file mode 100644 index 0000000000..7ceb726e2b --- /dev/null +++ b/avm/res/hybrid-compute/machine/main.bicep @@ -0,0 +1,256 @@ +metadata name = 'Hybrid Compute Machines' +metadata description = 'This module deploys an Arc Machine for use with Arc Resource Bridge for Azure Stack HCI or VMware. In these scenarios, this resource module will be used in combination with another resource module to create the require Virtual Machine Instance extension resource on this Arc Machine resource. This module should not be used for other Arc-enabled server scenarios, where the Arc Machine resource is created automatically by the onboarding process.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the Arc machine to be created. You should use a unique prefix to reduce name collisions in Active Directory.') +param name string + +@description('Required. Kind of Arc machine to be created. Possible values are: HCI, SCVMM, VMware.') +param kind string + +@description('Conditional. The resource ID of an Arc Private Link Scope which which to associate this machine. Required if you are using Private Link for Arc and your Arc Machine will resolve a Private Endpoint for connectivity to Azure.') +param privateLinkScopeResourceId string = '' + +@description('Optional. Parent cluster resource ID (Azure Stack HCI).') +param parentClusterResourceId string = '' + +@description('Optional. The GUID of the on-premises virtual machine from your hypervisor.') +param vmId string = '' + +@description('Optional. The Public Key that the client provides to be used during initial resource onboarding.') +@secure() +param clientPublicKey string = '' + +@description('Optional. VM guest patching orchestration mode. \'AutomaticByOS\' & \'Manual\' are for Windows only, \'ImageDefault\' for Linux only.') +@allowed([ + 'AutomaticByPlatform' + 'AutomaticByOS' + 'Manual' + 'ImageDefault' +]) +param patchMode string? + +@description('Optional. VM guest patching assessment mode. Set it to \'AutomaticByPlatform\' to enable automatically check for updates every 24 hours.') +@allowed([ + 'AutomaticByPlatform' + 'ImageDefault' +]) +param patchAssessmentMode string = 'ImageDefault' + +// support added in 2024-05-20-preview +//@description('Optional. Captures the hotpatch capability enrollment intent of the customers, which enables customers to patch their Windows machines without requiring a reboot.') +//param enableHotpatching bool = false + +// Child resources +@description('Optional. The guest configuration for the Arc machine. Needs the Guest Configuration extension to be enabled.') +param guestConfiguration object = {} + +@description('Conditional. Required if you are providing OS-type specified configurations, such as patch settings. The chosen OS type, either Windows or Linux.') +@allowed([ + 'Windows' + 'Linux' +]) +param osType string? + +// Shared parameters +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var linuxConfiguration = { + patchSettings: (patchMode == 'AutomaticByPlatform' || patchMode == 'ImageDefault') + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + } + : null +} + +var windowsConfiguration = { + patchSettings: (patchMode == 'AutomaticByPlatform' || patchMode == 'AutomaticByOS' || patchMode == 'Manual') + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + // enableHotpatching: enableHotpatching // support added in 2024-05-20-preview + } + : 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') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) + 'Arc machine Administrator Login': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '1c0163c0-47e6-4577-8991-ea5c82e286e4' + ) + 'Arc machine Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + ) + 'Arc machine User Login': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'fb879df8-f326-4884-b1cf-06f3ad86be52' + ) + 'Windows Admin Center Administrator Login': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'a6333a3e-0164-44c3-b281-7a577aff287f' + ) +} + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.res.hybridcompute-machine.${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' + } + } + } + } +} + +resource machine 'Microsoft.HybridCompute/machines@2024-03-31-preview' = { + name: name + location: location + identity: { + type: 'SystemAssigned' + } + tags: tags + kind: kind + properties: { + osProfile: { + windowsConfiguration: osType == 'Windows' ? windowsConfiguration : null + linuxConfiguration: osType == 'Linux' ? linuxConfiguration : null + } + parentClusterResourceId: parentClusterResourceId + vmId: vmId + clientPublicKey: clientPublicKey + privateLinkScopeResourceId: !empty(privateLinkScopeResourceId) ? privateLinkScopeResourceId : null + } +} + +resource AzureWindowsBaseline 'Microsoft.GuestConfiguration/guestConfigurationAssignments@2020-06-25' = if (!empty(guestConfiguration)) { + name: 'gca-${name}' + scope: machine + dependsOn: [] + location: location + properties: { + guestConfiguration: guestConfiguration + } +} + +resource machine_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: machine +} + +resource machine_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(machine.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + 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: machine + } +] + +@description('The name of the machine.') +output name string = machine.name + +@description('The resource ID of the machine.') +output resourceId string = machine.id + +@description('The name of the resource group the VM was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = machine.?identity.?principalId ?? '' + +@description('The location the resource was deployed into.') +output location string = machine.location + +// =============== // +// 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('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/hybrid-compute/machine/main.json b/avm/res/hybrid-compute/machine/main.json new file mode 100644 index 0000000000..a2151e8063 --- /dev/null +++ b/avm/res/hybrid-compute/machine/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.28.1.47646", + "templateHash": "6825923126291924605" + }, + "name": "Hybrid Compute Machines", + "description": "This module deploys an Arc Machine for use with Arc Resource Bridge for Azure Stack HCI or VMware. In these scenarios, this resource module will be used in combination with another resource module to create the require Virtual Machine Instance extension resource on this Arc Machine resource. This module should not be used for other Arc-enabled server scenarios, where the Arc Machine resource is created automatically by the onboarding process.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Arc machine to be created. You should use a unique prefix to reduce name collisions in Active Directory." + } + }, + "kind": { + "type": "string", + "metadata": { + "description": "Required. Kind of Arc machine to be created. Possible values are: HCI, SCVMM, VMware." + } + }, + "privateLinkScopeResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of an Arc Private Link Scope which which to associate this machine. Required if you are using Private Link for Arc and your Arc Machine will resolve a Private Endpoint for connectivity to Azure." + } + }, + "parentClusterResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Parent cluster resource ID (Azure Stack HCI)." + } + }, + "vmId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The GUID of the on-premises virtual machine from your hypervisor." + } + }, + "clientPublicKey": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. The Public Key that the client provides to be used during initial resource onboarding." + } + }, + "patchMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "AutomaticByPlatform", + "AutomaticByOS", + "Manual", + "ImageDefault" + ], + "metadata": { + "description": "Optional. VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only." + } + }, + "patchAssessmentMode": { + "type": "string", + "defaultValue": "ImageDefault", + "allowedValues": [ + "AutomaticByPlatform", + "ImageDefault" + ], + "metadata": { + "description": "Optional. VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours." + } + }, + "guestConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The guest configuration for the Arc machine. Needs the Guest Configuration extension to be enabled." + } + }, + "osType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Windows", + "Linux" + ], + "metadata": { + "description": "Conditional. Required if you are providing OS-type specified configurations, such as patch settings. The chosen OS type, either Windows or Linux." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "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." + } + } + }, + "variables": { + "linuxConfiguration": { + "patchSettings": "[if(or(equals(parameters('patchMode'), 'AutomaticByPlatform'), equals(parameters('patchMode'), 'ImageDefault')), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode')), null())]" + }, + "windowsConfiguration": { + "patchSettings": "[if(or(or(equals(parameters('patchMode'), 'AutomaticByPlatform'), equals(parameters('patchMode'), 'AutomaticByOS')), equals(parameters('patchMode'), 'Manual')), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode')), 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')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Arc machine Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", + "Arc machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Arc machine User Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52')]", + "Windows Admin Center Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.hybridcompute-machine.{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" + } + } + } + } + }, + "machine": { + "type": "Microsoft.HybridCompute/machines", + "apiVersion": "2024-03-31-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "osProfile": { + "windowsConfiguration": "[if(equals(parameters('osType'), 'Windows'), variables('windowsConfiguration'), null())]", + "linuxConfiguration": "[if(equals(parameters('osType'), 'Linux'), variables('linuxConfiguration'), null())]" + }, + "parentClusterResourceId": "[parameters('parentClusterResourceId')]", + "vmId": "[parameters('vmId')]", + "clientPublicKey": "[parameters('clientPublicKey')]", + "privateLinkScopeResourceId": "[if(not(empty(parameters('privateLinkScopeResourceId'))), parameters('privateLinkScopeResourceId'), null())]" + } + }, + "AzureWindowsBaseline": { + "condition": "[not(empty(parameters('guestConfiguration')))]", + "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments", + "apiVersion": "2020-06-25", + "scope": "[format('Microsoft.HybridCompute/machines/{0}', parameters('name'))]", + "name": "[format('gca-{0}', parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "guestConfiguration": "[parameters('guestConfiguration')]" + }, + "dependsOn": [ + "machine" + ] + }, + "machine_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/machines/{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": [ + "machine" + ] + }, + "machine_roleAssignments": { + "copy": { + "name": "machine_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.HybridCompute/machines/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.HybridCompute/machines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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')]" + }, + "dependsOn": [ + "machine" + ] + } + }, + "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/machines', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the VM was created in." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('machine', '2024-03-31-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('machine', '2024-03-31-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/hybrid-compute/machine/tests/e2e/hci.defaults/main.test.bicep b/avm/res/hybrid-compute/machine/tests/e2e/hci.defaults/main.test.bicep new file mode 100644 index 0000000000..66b423867d --- /dev/null +++ b/avm/res/hybrid-compute/machine/tests/e2e/hci.defaults/main.test.bicep @@ -0,0 +1,48 @@ +targetScope = 'subscription' + +metadata name = 'Creates an Arc Machine 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.machine-${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 = 'arcmachcimin' + +@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: { + location: resourceLocation + name: '${namePrefix}${serviceShort}' + kind: 'HCI' + } + } +] diff --git a/avm/res/hybrid-compute/machine/tests/e2e/max.hci/dependencies.bicep b/avm/res/hybrid-compute/machine/tests/e2e/max.hci/dependencies.bicep new file mode 100644 index 0000000000..f482c45c67 --- /dev/null +++ b/avm/res/hybrid-compute/machine/tests/e2e/max.hci/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Required. The name of the Private Link Scope to create.') +param privateLinkScopeName string + +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +resource privateLinkScope 'Microsoft.HybridCompute/privateLinkScopes@2023-10-03-preview' = { + name: privateLinkScopeName + location: location + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + properties: { + publicNetworkAccess: 'Enabled' + } +} + +@description('The resource ID of the created Private Link Scope.') +output privateLinkScopeResourceId string = privateLinkScope.id diff --git a/avm/res/hybrid-compute/machine/tests/e2e/max.hci/main.test.bicep b/avm/res/hybrid-compute/machine/tests/e2e/max.hci/main.test.bicep new file mode 100644 index 0000000000..7f9d565e22 --- /dev/null +++ b/avm/res/hybrid-compute/machine/tests/e2e/max.hci/main.test.bicep @@ -0,0 +1,89 @@ +targetScope = 'subscription' + +metadata name = 'Creates an Arc Machine with maximum configurations' +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}-hybridCompute.machine-${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 = 'arcmachcimx' + +@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 + privateLinkScopeName: 'dep-${namePrefix}-pls-${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: { + location: resourceLocation + name: '${namePrefix}${serviceShort}' + kind: 'HCI' + patchAssessmentMode: 'AutomaticByPlatform' + patchMode: 'AutomaticByPlatform' + privateLinkScopeResourceId: nestedDependencies.outputs.privateLinkScopeResourceId + guestConfiguration: { + name: 'AzureWindowsBaseline' + version: '1.*' + assignmentType: 'ApplyAndMonitor' + configurationParameter: [ + { + name: 'Minimum Password Length;ExpectedValue' + value: '16' + } + { + name: 'Minimum Password Length;RemediateValue' + value: '16' + } + { + name: 'Maximum Password Age;ExpectedValue' + value: '75' + } + { + name: 'Maximum Password Age;RemediateValue' + value: '75' + } + ] + } + osType: 'Windows' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/hybrid-compute/machine/tests/e2e/vmware.defaults/main.test.bicep b/avm/res/hybrid-compute/machine/tests/e2e/vmware.defaults/main.test.bicep new file mode 100644 index 0000000000..524d3881ec --- /dev/null +++ b/avm/res/hybrid-compute/machine/tests/e2e/vmware.defaults/main.test.bicep @@ -0,0 +1,49 @@ +targetScope = 'subscription' + +metadata name = 'Creates an VMWare machine 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.machine-${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 = 'arcmacvmwmin' + +@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: { + location: resourceLocation + name: '${namePrefix}${serviceShort}' + kind: 'VMware' + } + } +] diff --git a/avm/res/hybrid-compute/machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/hybrid-compute/machine/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..f9932ecb3e --- /dev/null +++ b/avm/res/hybrid-compute/machine/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,53 @@ +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}-hybridCompute.machine-${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 = 'arcmacwaf' + +@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: { + location: resourceLocation + name: '${namePrefix}${serviceShort}' + kind: 'HCI' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/hybrid-compute/machine/version.json b/avm/res/hybrid-compute/machine/version.json new file mode 100644 index 0000000000..0200aa0775 --- /dev/null +++ b/avm/res/hybrid-compute/machine/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 26de2f7d59..6931df0373 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -2694,7 +2694,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". | | [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | **Optional parameters** @@ -2706,19 +2706,21 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | -| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | Manual PrivateLink Service Connections. | +| [`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. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`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. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | ### Parameter: `privateEndpoints.service` -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". +The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". - Required: Yes - Type: string @@ -2838,7 +2840,7 @@ A private ip address obtained from the private endpoint's subnet. ### Parameter: `privateEndpoints.isManualConnection` -Manual PrivateLink Service Connections. +If Manual Private Link Connection is required. - Required: No - Type: bool @@ -2914,6 +2916,20 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments` Array of role assignments to create. diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index d2e7e2864d..67f2dcc12e 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -476,10 +476,11 @@ resource storageAccount_roleAssignments 'Microsoft.Authorization/roleAssignments module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-StorageAccount-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-storageAccount-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' - privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' @@ -492,7 +493,7 @@ module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoi } ] : null - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' @@ -713,7 +714,10 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Required. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string @description('Required. Resource ID of the subnet where the endpoint needs to be created.') @@ -725,7 +729,7 @@ type privateEndpointType = { @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') privateDnsZoneResourceIds: string[]? - @description('Optional. Manual PrivateLink Service Connections.') + @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.') @@ -776,6 +780,9 @@ type privateEndpointType = { @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 = { diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 4624ad705b..fb8ad05467 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.28.1.47646", - "templateHash": "11817355272215191914" + "templateHash": "9679329912314181282" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -217,10 +217,17 @@ "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", "metadata": { - "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." } }, "subnetResourceId": { @@ -250,7 +257,7 @@ "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." + "description": "Optional. If Manual Private Link Connection is required." } }, "manualConnectionRequestMessage": { @@ -375,6 +382,13 @@ "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." + } } } }, @@ -1023,7 +1037,8 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-StorageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1033,8 +1048,8 @@ "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "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.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", "subnetResourceId": { "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index eb60880311..cbc4919670 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -27,6 +27,7 @@ This module deploys a Synapse Workspace. | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Synapse/workspaces` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces) | +| `Microsoft.Synapse/workspaces/administrators` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/administrators) | | `Microsoft.Synapse/workspaces/integrationRuntimes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/integrationRuntimes) | | `Microsoft.Synapse/workspaces/keys` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) | @@ -346,6 +347,11 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { name: 'swmax001' sqlAdministratorLogin: 'synwsadmin' // Non-required parameters + administrator: { + administratorType: 'ServicePrincipal' + login: 'dep-msi-swmax' + sid: '' + } diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -469,6 +475,13 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { "value": "synwsadmin" }, // Non-required parameters + "administrator": { + "value": { + "administratorType": "ServicePrincipal", + "login": "dep-msi-swmax", + "sid": "" + } + }, "diagnosticSettings": { "value": [ { @@ -758,6 +771,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`accountUrl`](#parameter-accounturl) | string | The account URL of the data lake storage account. | +| [`administrator`](#parameter-administrator) | object | The Entra ID administrator for the synapse workspace. | | [`allowedAadTenantIdsForLinking`](#parameter-allowedaadtenantidsforlinking) | array | Allowed AAD Tenant IDs For Linking. | | [`azureADOnlyAuthentication`](#parameter-azureadonlyauthentication) | bool | Enable or Disable AzureADOnlyAuthentication on All Workspace sub-resource. | | [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | @@ -818,6 +832,55 @@ The account URL of the data lake storage account. - Type: string - Default: `[format('https://{0}.dfs.{1}', last(split(parameters('defaultDataLakeStorageAccountResourceId'), '/')), environment().suffixes.storage)]` +### Parameter: `administrator` + +The Entra ID administrator for the synapse workspace. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorType`](#parameter-administratoradministratortype) | string | Workspace active directory administrator type. | +| [`login`](#parameter-administratorlogin) | securestring | Login of the workspace active directory administrator. | +| [`sid`](#parameter-administratorsid) | securestring | Object ID of the workspace active directory administrator. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`tenantId`](#parameter-administratortenantid) | securestring | Tenant ID of the workspace active directory administrator. | + +### Parameter: `administrator.administratorType` + +Workspace active directory administrator type. + +- Required: Yes +- Type: string + +### Parameter: `administrator.login` + +Login of the workspace active directory administrator. + +- Required: Yes +- Type: securestring + +### Parameter: `administrator.sid` + +Object ID of the workspace active directory administrator. + +- Required: Yes +- Type: securestring + +### Parameter: `administrator.tenantId` + +Tenant ID of the workspace active directory administrator. + +- Required: No +- Type: securestring + ### Parameter: `allowedAadTenantIdsForLinking` Allowed AAD Tenant IDs For Linking. diff --git a/avm/res/synapse/workspace/administrators/README.md b/avm/res/synapse/workspace/administrators/README.md new file mode 100644 index 0000000000..07ebda7b00 --- /dev/null +++ b/avm/res/synapse/workspace/administrators/README.md @@ -0,0 +1,92 @@ +# Synapse Workspaces Administrators `[Microsoft.Synapse/workspaces/administrators]` + +This module deploys Synapse Workspaces Administrators. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Synapse/workspaces/administrators` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/administrators) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorType`](#parameter-administratortype) | string | Workspace active directory administrator type. | +| [`login`](#parameter-login) | securestring | Login of the workspace active directory administrator. | +| [`sid`](#parameter-sid) | securestring | Object ID of the workspace active directory administrator. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`workspaceName`](#parameter-workspacename) | string | The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`tenantId`](#parameter-tenantid) | string | Tenant ID of the workspace active directory administrator. | + +### Parameter: `administratorType` + +Workspace active directory administrator type. + +- Required: Yes +- Type: string + +### Parameter: `login` + +Login of the workspace active directory administrator. + +- Required: Yes +- Type: securestring + +### Parameter: `sid` + +Object ID of the workspace active directory administrator. + +- Required: Yes +- Type: securestring + +### Parameter: `workspaceName` + +The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `tenantId` + +Tenant ID of the workspace active directory administrator. + +- Required: No +- Type: string +- Default: `[tenant().tenantId]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed administrator. | +| `resourceGroupName` | string | The resource group of the deployed administrator. | +| `resourceId` | string | The resource ID of the deployed administrator. | + +## Cross-referenced modules + +_None_ + +## 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/synapse/workspace/administrators/main.bicep b/avm/res/synapse/workspace/administrators/main.bicep new file mode 100644 index 0000000000..c6410bb361 --- /dev/null +++ b/avm/res/synapse/workspace/administrators/main.bicep @@ -0,0 +1,44 @@ +metadata name = 'Synapse Workspaces Administrators' +metadata description = 'This module deploys Synapse Workspaces Administrators.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Required. Workspace active directory administrator type.') +param administratorType string + +@description('Required. Login of the workspace active directory administrator.') +@secure() +param login string + +@description('Required. Object ID of the workspace active directory administrator.') +@secure() +param sid string + +@description('Optional. Tenant ID of the workspace active directory administrator.') +param tenantId string = tenant().tenantId + +resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { + name: workspaceName +} + +resource synapse_workspace_administrator 'Microsoft.Synapse/workspaces/administrators@2021-06-01' = { + name: 'activeDirectory' + parent: workspace + properties: { + administratorType: administratorType + login: login + sid: sid + tenantId: tenantId + } +} + +@description('The name of the deployed administrator.') +output name string = synapse_workspace_administrator.name + +@description('The resource ID of the deployed administrator.') +output resourceId string = synapse_workspace_administrator.id + +@description('The resource group of the deployed administrator.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/synapse/workspace/administrators/main.json b/avm/res/synapse/workspace/administrators/main.json new file mode 100644 index 0000000000..76087e4632 --- /dev/null +++ b/avm/res/synapse/workspace/administrators/main.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "351030244990389212" + }, + "name": "Synapse Workspaces Administrators", + "description": "This module deploys Synapse Workspaces Administrators.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "administratorType": { + "type": "string", + "metadata": { + "description": "Required. Workspace active directory administrator type." + } + }, + "login": { + "type": "securestring", + "metadata": { + "description": "Required. Login of the workspace active directory administrator." + } + }, + "sid": { + "type": "securestring", + "metadata": { + "description": "Required. Object ID of the workspace active directory administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "Optional. Tenant ID of the workspace active directory administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/administrators", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), 'activeDirectory')]", + "properties": { + "administratorType": "[parameters('administratorType')]", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed administrator." + }, + "value": "activeDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed administrator." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/administrators', parameters('workspaceName'), 'activeDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed administrator." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index 086fd3eb92..538751229e 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -31,6 +31,9 @@ param defaultDataLakeStorageFilesystem string @description('Optional. Create managed private endpoint to the default storage account or not. If Yes is selected, a managed private endpoint connection request is sent to the workspace\'s primary Data Lake Storage Gen2 account for Spark pools to access data. This must be approved by an owner of the storage account.') param defaultDataLakeStorageCreateManagedPrivateEndpoint bool = false +@description('Optional. The Entra ID administrator for the synapse workspace.') +param administrator adminType + @description('Optional. The customer managed key definition.') param customerManagedKey customerManagedKeyType @@ -274,6 +277,18 @@ module workspace_key 'key/main.bicep' = if (encryptionActivateWorkspace) { ] } +// - Workspace Entra ID Administrator +module workspace_administrator 'administrators/main.bicep' = if (!empty(administrator)) { + name: '${workspace.name}-administrator' + params: { + workspaceName: workspace.name + administratorType: administrator!.administratorType + login: administrator!.login + sid: administrator!.sid + tenantId: administrator.?tenantId + } +} + // Resource Lock resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { name: lock.?name ?? 'lock-${name}' @@ -564,3 +579,20 @@ type customerManagedKeyType = { @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') userAssignedIdentityResourceId: string? }? + +type adminType = { + @description('Required. Workspace active directory administrator type.') + administratorType: string + + @description('Required. Login of the workspace active directory administrator.') + @secure() + login: string + + @description('Required. Object ID of the workspace active directory administrator.') + @secure() + sid: string + + @description('Optional. Tenant ID of the workspace active directory administrator.') + @secure() + tenantId: string? +}? diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index fef9827d54..4b0570811e 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "8395600145628095339" + "templateHash": "16827959448836859226" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -434,6 +434,37 @@ } }, "nullable": true + }, + "adminType": { + "type": "object", + "properties": { + "administratorType": { + "type": "string", + "metadata": { + "description": "Required. Workspace active directory administrator type." + } + }, + "login": { + "type": "securestring", + "metadata": { + "description": "Required. Login of the workspace active directory administrator." + } + }, + "sid": { + "type": "securestring", + "metadata": { + "description": "Required. Object ID of the workspace active directory administrator." + } + }, + "tenantId": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Tenant ID of the workspace active directory administrator." + } + } + }, + "nullable": true } }, "parameters": { @@ -498,6 +529,12 @@ "description": "Optional. Create managed private endpoint to the default storage account or not. If Yes is selected, a managed private endpoint connection request is sent to the workspace's primary Data Lake Storage Gen2 account for Spark pools to access data. This must be approved by an owner of the storage account." } }, + "administrator": { + "$ref": "#/definitions/adminType", + "metadata": { + "description": "Optional. The Entra ID administrator for the synapse workspace." + } + }, "customerManagedKey": { "$ref": "#/definitions/customerManagedKeyType", "metadata": { @@ -1079,6 +1116,121 @@ "workspace_cmk_rbac" ] }, + "workspace_administrator": { + "condition": "[not(empty(parameters('administrator')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-administrator', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "workspaceName": { + "value": "[parameters('name')]" + }, + "administratorType": { + "value": "[parameters('administrator').administratorType]" + }, + "login": { + "value": "[parameters('administrator').login]" + }, + "sid": { + "value": "[parameters('administrator').sid]" + }, + "tenantId": { + "value": "[tryGet(parameters('administrator'), 'tenantId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "351030244990389212" + }, + "name": "Synapse Workspaces Administrators", + "description": "This module deploys Synapse Workspaces Administrators.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "administratorType": { + "type": "string", + "metadata": { + "description": "Required. Workspace active directory administrator type." + } + }, + "login": { + "type": "securestring", + "metadata": { + "description": "Required. Login of the workspace active directory administrator." + } + }, + "sid": { + "type": "securestring", + "metadata": { + "description": "Required. Object ID of the workspace active directory administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "Optional. Tenant ID of the workspace active directory administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/administrators", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), 'activeDirectory')]", + "properties": { + "administratorType": "[parameters('administratorType')]", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed administrator." + }, + "value": "activeDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed administrator." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/administrators', parameters('workspaceName'), 'activeDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed administrator." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "workspace" + ] + }, "workspace_privateEndpoints": { "copy": { "name": "workspace_privateEndpoints", diff --git a/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep b/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep index 682ca135f8..ff749a63ba 100644 --- a/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep @@ -77,6 +77,12 @@ module testDeployment '../../../main.bicep' = [ nestedDependencies.outputs.managedIdentityResourceId ] } + administrator: { + administratorType: 'ServicePrincipal' + login: 'dep-${namePrefix}-msi-${serviceShort}' + sid: nestedDependencies.outputs.managedIdentityPrincipalId + } + roleAssignments: [ { roleDefinitionIdOrName: 'Owner' diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 8cf41b7807..82e59d5e52 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -417,6 +417,13 @@ The name of the SKU will Determine the tier, size, family of the App Service Pla - Required: Yes - Type: string +- Example: + ```Bicep + 'F1' + 'B1' + 'P1v3' + 'I1v2' + ``` ### Parameter: `reserved` diff --git a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 index 673e5929fe..49e70020b8 100644 --- a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 +++ b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 @@ -330,6 +330,7 @@ function Set-DefinitionSection { $isRequired = (Get-IsParameterRequired -TemplateFileContent $TemplateFileContent -Parameter $parameter) ? 'Yes' : 'No' $description = $parameter.ContainsKey('metadata') ? $parameter['metadata']['description'].substring("$category. ".Length).Replace("`n- ", '

  • ').Replace("`r`n", '

    ').Replace("`n", '

    ') : $null + $example = ($parameter.ContainsKey('metadata') -and $parameter['metadata'].ContainsKey('example')) ? $parameter['metadata']['example'] : $null ##################### # Table content # @@ -412,6 +413,29 @@ function Set-DefinitionSection { $formattedAllowedValues = $null } + # Format example + # ============== + if (-not [String]::IsNullOrEmpty($example)) { + # allign content to the left by removing trailing whitespaces + $leadingSpacesToTrim = ($example -match '^(\s+).+') ? $matches[1].Length : 0 + $exampleLines = $example -split '\n' + # Removing excess leading spaces + $example = ($exampleLines | Where-Object { -not [String]::IsNullOrEmpty($_) } | ForEach-Object { " $_" -replace "^\s{$leadingSpacesToTrim}" } | Out-String).TrimEnd() + + if ($exampleLines.count -eq 1) { + $formattedExample = '- Example: `{0}`' -f $example.TrimStart() + } else { + $formattedExample = @( + '- Example:', + ' ```Bicep', + $example, + ' ```' + ) + } + } else { + $formattedExample = $null + } + # Build list item # =============== $listSectionContent += @( @@ -422,7 +446,8 @@ function Set-DefinitionSection { ('- Required: {0}' -f $isRequired), ('- Type: {0}' -f $type), ((-not [String]::IsNullOrEmpty($formattedDefaultValue)) ? $formattedDefaultValue : $null), - ((-not [String]::IsNullOrEmpty($formattedAllowedValues)) ? $formattedAllowedValues : $null) + ((-not [String]::IsNullOrEmpty($formattedAllowedValues)) ? $formattedAllowedValues : $null), + ((-not [String]::IsNullOrEmpty($formattedExample)) ? $formattedExample : $null) '' ) | Where-Object { $null -ne $_ } diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml index a6881b3844..5e9c67b778 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml @@ -31,6 +31,7 @@ spec: - Azure.ContainerApp.PublicAccess # Azure Virtual Machine - Azure.VM.AMA + - Azure.VM.MaintenanceConfig # Excluded as it requires user input - Azure.VM.Standalone # Azure App Service - Azure.AppService.WebProbe # Supressed as the probe path is specific to the app diff --git a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml index 9c163ed9dd..8db91d89ca 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml @@ -81,4 +81,4 @@ rule: - Azure.VM.UseHybridUseBenefit - Azure.Storage.UseReplication - Azure.AppConfig.PurgeProtect - - Azure.APIM.MultiRegion + - Azure.APIM.MultiRegion # Team agreed this is too expensive for most use cases and is safe to ignore.