diff --git a/.azuredevops/modulePipelines/ms.synapse.privatelinkhubs.yml b/.azuredevops/modulePipelines/ms.synapse.privatelinkhubs.yml new file mode 100644 index 0000000000..65fb083c1b --- /dev/null +++ b/.azuredevops/modulePipelines/ms.synapse.privatelinkhubs.yml @@ -0,0 +1,63 @@ +name: 'Synapse - PrivateLinkHubs' + +parameters: + - name: removeDeployment + displayName: Remove deployed module + type: boolean + default: true + - name: versioningOption + displayName: The mode to handle the version increments [major|minor|patch] + type: string + default: patch + values: + - patch + - minor + - major + - name: customVersion + displayName: Custom version to apply. Used only if higher than latest + type: string + default: '0.0.1' + +trigger: + batch: true + branches: + include: + - main + paths: + include: + - '/.azuredevops/modulePipelines/ms.synapse.privatelinkhubs.yml' + - '/.azuredevops/pipelineTemplates/module.*.yml' + - '/arm/Microsoft.Synapse/privateLinkHubs/*' + exclude: + - '/**/*.md' + +variables: + - template: '/.azuredevops/pipelineVariables/global.variables.yml' + - group: 'PLATFORM_VARIABLES' + - name: modulePath + value: '/arm/Microsoft.Synapse/privateLinkHubs' + +stages: + - stage: Validation + displayName: Pester tests + jobs: + - template: /.azuredevops/pipelineTemplates/module.jobs.validate.yml + + - stage: Deployment + displayName: Deployment tests + jobs: + - template: /.azuredevops/pipelineTemplates/module.jobs.deploy.yml + parameters: + removeDeployment: '${{ parameters.removeDeployment }}' + deploymentBlocks: + - path: $(modulePath)/.parameters/min.parameters.json + - path: $(modulePath)/.parameters/parameters.json + + - stage: Publishing + displayName: Publish module + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) + jobs: + - template: /.azuredevops/pipelineTemplates/module.jobs.publish.yml + parameters: + versioningOption: '${{ parameters.versioningOption }}' + customVersion: '${{ parameters.customVersion }}' diff --git a/.github/workflows/ms.synapse.privatelinkhubs.yml b/.github/workflows/ms.synapse.privatelinkhubs.yml new file mode 100644 index 0000000000..85a7334ec9 --- /dev/null +++ b/.github/workflows/ms.synapse.privatelinkhubs.yml @@ -0,0 +1,146 @@ +name: 'Synapse: PrivateLinkHubs' + +on: + workflow_dispatch: + inputs: + removeDeployment: + type: boolean + description: 'Remove deployed module' + required: false + default: 'true' + versioningOption: + type: choice + description: 'The mode to handle the version increments [major|minor|patch]' + required: false + default: 'patch' + options: + - major + - minor + - patch + customVersion: + description: 'Custom version to apply. Used only if higher than latest' + required: false + default: '0.0.1' + push: + branches: + - main + paths: + - '.github/actions/templates/**' + - '.github/workflows/ms.synapse.privatelinkhubs.yml' + - 'arm/Microsoft.Synapse/privateLinkHubs/**' + - '!*/**/readme.md' + - 'utilities/pipelines/**' + - '!utilities/pipelines/dependencies/**' + +env: + modulePath: 'arm/Microsoft.Synapse/privateLinkHubs' + workflowPath: '.github/workflows/ms.synapse.privatelinkhubs.yml' + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + ARM_SUBSCRIPTION_ID: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + ARM_MGMTGROUP_ID: '${{ secrets.ARM_MGMTGROUP_ID }}' + ARM_TENANT_ID: '${{ secrets.ARM_TENANT_ID }}' + DEPLOYMENT_SP_ID: '${{ secrets.DEPLOYMENT_SP_ID }}' + +jobs: + ############################ + # SET INPUT PARAMETERS # + ############################ + job_set_workflow_param: + runs-on: ubuntu-20.04 + name: 'Set input parameters to output variables' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: 'Set input parameters' + id: get-workflow-param + uses: ./.github/actions/templates/getWorkflowInput + with: + workflowPath: '${{ env.workflowPath}}' + outputs: + removeDeployment: ${{ steps.get-workflow-param.outputs.removeDeployment }} + versioningOption: ${{ steps.get-workflow-param.outputs.versioningOption }} + customVersion: ${{ steps.get-workflow-param.outputs.customVersion }} + + #################### + # Pester Tests # + #################### + job_module_pester_validation: + runs-on: ubuntu-20.04 + name: 'Pester tests' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: 'Run tests' + uses: ./.github/actions/templates/validateModulePester + with: + modulePath: '${{ env.modulePath }}' + + #################### + # Deployment tests # + #################### + job_module_deploy_validation: + runs-on: ubuntu-20.04 + name: 'Deployment tests' + needs: + - job_set_workflow_param + - job_module_pester_validation + strategy: + fail-fast: false + matrix: + parameterFilePaths: ['min.parameters.json', 'parameters.json'] + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: deep-mm/set-variables@v1.0 + with: + variableFileName: 'global.variables' + - name: 'Using parameter file [${{ matrix.parameterFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + parameterFilePath: '${{ env.modulePath }}/.parameters/${{ matrix.parameterFilePaths }}' + location: '${{ env.defaultLocation }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_set_workflow_param.outputs.removeDeployment }}' + + ############### + # PUBLISH # + ############### + job_publish_module: + name: 'Publish module' + if: contains(fromJson('["refs/heads/main", "refs/heads/master"]'), github.ref) + runs-on: ubuntu-20.04 + needs: + - job_set_workflow_param + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: deep-mm/set-variables@v1.0 + with: + variableFileName: 'global.variables' + - name: 'Publish module' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + versioningOption: '${{ needs.job_set_workflow_param.outputs.versioningOption }}' + customVersion: '${{ needs.job_set_workflow_param.outputs.customVersion }}' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.vscode/settings.json b/.vscode/settings.json index 8b56df2cb7..deea9cfa6c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,45 +1,43 @@ { - "[bicep]": { - "editor.insertSpaces": true, - "editor.tabSize": 2 - }, - "[markdown]": { - "files.encoding": "utf8" - }, - "[powershell]": { - "editor.insertSpaces": true, - "editor.tabSize": 4, - "files.encoding": "utf8bom" - }, - "[yaml]": { - "editor.insertSpaces": true, - "editor.tabSize": 2 - }, - "editor.formatOnPaste": true, - "editor.formatOnSave": true, + "[bicep]": { "editor.insertSpaces": true, - "files.insertFinalNewline": true, - "files.trimTrailingWhitespace": true, - "markdown.extension.orderedList.marker": "one", - "markdown.extension.tableFormatter.enabled": false, - "markdownlint.config": { - "MD034": true - }, - "powershell.codeFormatting.autoCorrectAliases": true, - "powershell.codeFormatting.newLineAfterCloseBrace": false, - "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", - "powershell.codeFormatting.preset": "OTBS", - "powershell.codeFormatting.trimWhitespaceAroundPipe": true, - "powershell.codeFormatting.useConstantStrings": true, - "powershell.codeFormatting.useCorrectCasing": true, - "powershell.codeFormatting.whitespaceBetweenParameters": true, - "spellright.documentTypes": [ - "markdown", - "latex", - "plaintext" - ], - "spellright.language": [ - "en" - ], - "yaml.format.singleQuote": true + "editor.tabSize": 2 + }, + "[markdown]": { + "files.encoding": "utf8" + }, + "[powershell]": { + "editor.insertSpaces": true, + "editor.tabSize": 4, + "files.encoding": "utf8bom" + }, + "[yaml]": { + "editor.insertSpaces": true, + "editor.tabSize": 2 + }, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.insertSpaces": true, + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "markdown.extension.orderedList.marker": "one", + "markdown.extension.tableFormatter.enabled": false, + "markdownlint.config": { + "MD034": true, + "MD028": false, + "MD025": { + "front_matter_title": "" + } + }, + "powershell.codeFormatting.autoCorrectAliases": true, + "powershell.codeFormatting.newLineAfterCloseBrace": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.codeFormatting.preset": "OTBS", + "powershell.codeFormatting.trimWhitespaceAroundPipe": true, + "powershell.codeFormatting.useConstantStrings": true, + "powershell.codeFormatting.useCorrectCasing": true, + "powershell.codeFormatting.whitespaceBetweenParameters": true, + "spellright.documentTypes": ["markdown", "latex", "plaintext"], + "spellright.language": ["en"], + "yaml.format.singleQuote": true } diff --git a/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_cuaId.bicep b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_cuaId.bicep new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_cuaId.bicep @@ -0,0 +1 @@ + diff --git a/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_privateEndpoint.bicep b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_privateEndpoint.bicep new file mode 100644 index 0000000000..a65adc0f7d --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_privateEndpoint.bicep @@ -0,0 +1,50 @@ +param privateEndpointResourceId string +param privateEndpointVnetLocation string +param privateEndpointObj object +param tags object + +var privateEndpointResourceName = last(split(privateEndpointResourceId, '/')) +var privateEndpoint_var = { + name: contains(privateEndpointObj, 'name') ? (empty(privateEndpointObj.name) ? '${privateEndpointResourceName}-${privateEndpointObj.service}' : privateEndpointObj.name) : '${privateEndpointResourceName}-${privateEndpointObj.service}' + subnetResourceId: privateEndpointObj.subnetResourceId + service: [ + privateEndpointObj.service + ] + privateDnsZoneResourceIds: contains(privateEndpointObj, 'privateDnsZoneResourceIds') ? (empty(privateEndpointObj.privateDnsZoneResourceIds) ? [] : privateEndpointObj.privateDnsZoneResourceIds) : [] + customDnsConfigs: contains(privateEndpointObj, 'customDnsConfigs') ? (empty(privateEndpointObj.customDnsConfigs) ? null : privateEndpointObj.customDnsConfigs) : null +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-03-01' = { + name: privateEndpoint_var.name + location: privateEndpointVnetLocation + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: privateEndpoint_var.name + properties: { + privateLinkServiceId: privateEndpointResourceId + groupIds: privateEndpoint_var.service + } + } + ] + manualPrivateLinkServiceConnections: [] + subnet: { + id: privateEndpoint_var.subnetResourceId + } + customDnsConfigs: privateEndpoint_var.customDnsConfigs + } +} + +resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-03-01' = if (!empty(privateEndpoint_var.privateDnsZoneResourceIds)) { + name: 'default' + properties: { + privateDnsZoneConfigs: [for privateDnsZoneResourceId in privateEndpoint_var.privateDnsZoneResourceIds: { + name: last(split(privateDnsZoneResourceId, '/')) + properties: { + privateDnsZoneId: privateDnsZoneResourceId + } + }] + } + parent: privateEndpoint +} diff --git a/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_rbac.bicep b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_rbac.bicep new file mode 100644 index 0000000000..0c750fea26 --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/.bicep/nested_rbac.bicep @@ -0,0 +1,32 @@ +param principalIds array +param roleDefinitionIdOrName string +param resourceId string + +var builtInRoleNames = { + 'Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource privateLinkHub 'Microsoft.Synapse/privateLinkHubs@2021-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = [for principalId in principalIds: { + name: guid(privateLinkHub.name, principalId, roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + } + scope: privateLinkHub +}] diff --git a/arm/Microsoft.Synapse/privateLinkHubs/.parameters/min.parameters.json b/arm/Microsoft.Synapse/privateLinkHubs/.parameters/min.parameters.json new file mode 100644 index 0000000000..8b3b8868ec --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/.parameters/min.parameters.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "synplhmin001" + } + } +} diff --git a/arm/Microsoft.Synapse/privateLinkHubs/.parameters/parameters.json b/arm/Microsoft.Synapse/privateLinkHubs/.parameters/parameters.json new file mode 100644 index 0000000000..35b74e6644 --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/.parameters/parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "synplhstandard001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": ["<>"] + }, + { + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "principalIds": ["<>"] + } + ] + } + } +} diff --git a/arm/Microsoft.Synapse/privateLinkHubs/deploy.bicep b/arm/Microsoft.Synapse/privateLinkHubs/deploy.bicep new file mode 100644 index 0000000000..1605f04c7b --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/deploy.bicep @@ -0,0 +1,68 @@ +@description('Required. The name of the Private Link Hub.') +param name string + +@description('Optional. The geo-location where the resource lives.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + 'CanNotDelete' + 'NotSpecified' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = 'NotSpecified' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'') +param roleAssignments array = [] + +@description('Optional. Configuration Details for private endpoints.') +param privateEndpoints array = [] + +resource privateLinkHub 'Microsoft.Synapse/privateLinkHubs@2021-06-01' = { + name: name + location: location + tags: tags +} + +// Resource Lock +resource privateLinkHub_lock 'Microsoft.Authorization/locks@2016-09-01' = if (lock != 'NotSpecified') { + name: '${privateLinkHub.name}-${lock}-lock' + properties: { + level: lock + notes: (lock == 'CanNotDelete') ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkHub +} + +// RBAC +module privateLinkHub_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-rbac-${index}' + params: { + principalIds: roleAssignment.principalIds + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: privateLinkHub.id + } +}] + +// Private Endpoints +module privateLinkHub_privateEndpoints '.bicep/nested_privateEndpoint.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-${index}' + params: { + privateEndpointResourceId: privateLinkHub.id + privateEndpointVnetLocation: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + privateEndpointObj: privateEndpoint + tags: tags + } +}] + +@description('The resource ID of the deployed Synapse Private Link Hub.') +output privateLinkHubResourceId string = privateLinkHub.id + +@description('The name of the deployed Synapse Private Link Hub.') +output privateLinkHubName string = privateLinkHub.name + +@description('The resource group of the deployed Synapse Private Link Hub.') +output privateLinkHubResourceGroup string = resourceGroup().name diff --git a/arm/Microsoft.Synapse/privateLinkHubs/readme.md b/arm/Microsoft.Synapse/privateLinkHubs/readme.md new file mode 100644 index 0000000000..71b1a5494c --- /dev/null +++ b/arm/Microsoft.Synapse/privateLinkHubs/readme.md @@ -0,0 +1,115 @@ +# Synapse PrivateLinkHubs `[Microsoft.Synapse/privateLinkHubs]` + +This module deploys Synapse PrivateLinkHubs. + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | 2016-09-01 | +| `Microsoft.Authorization/roleAssignments` | 2020-04-01-preview | +| `Microsoft.Network/privateEndpoints` | 2021-03-01 | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | 2021-03-01 | +| `Microsoft.Synapse/privateLinkHubs` | 2021-06-01 | + +## Parameters + +| Parameter Name | Type | Default Value | Possible Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `location` | string | `[resourceGroup().location]` | | Optional. The geo-location where the resource lives. | +| `lock` | string | `NotSpecified` | `[CanNotDelete, NotSpecified, ReadOnly]` | Optional. Specify the type of lock. | +| `name` | string | | | Required. The name of the Private Link Hub. | +| `privateEndpoints` | array | `[]` | | Optional. Configuration Details for private endpoints. | +| `roleAssignments` | array | `[]` | | Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' | +| `tags` | object | `{object}` | | Optional. Tags of the resource. | + +### Parameter Usage: `privateEndpoints` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "blob", + "privateDnsZoneResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ], + "customDnsConfigs": [ // Optional + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "file" + } + ] +} +``` + +### Parameter Usage: `roleAssignments` + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ] + } + ] +} +``` + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `privateLinkHubName` | string | The name of the deployed Synapse Private Link Hub. | +| `privateLinkHubResourceGroup` | string | The resource group of the deployed Synapse Private Link Hub. | +| `privateLinkHubResourceId` | string | The resource ID of the deployed Synapse Private Link Hub. | + +## Template references + +- [Locks](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2016-09-01/locks) +- [Privateendpoints](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-03-01/privateEndpoints) +- [Privateendpoints/Privatednszonegroups](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-03-01/privateEndpoints/privateDnsZoneGroups) +- [Privatelinkhubs](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/privateLinkHubs) +- [Roleassignments](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-04-01-preview/roleAssignments)