diff --git a/.github/actions/templates/setEnvironmentVariables/action.yml b/.github/actions/templates/setEnvironmentVariables/action.yml deleted file mode 100644 index 6685ed1..0000000 --- a/.github/actions/templates/setEnvironmentVariables/action.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: 'Set Environment Variables' -description: 'Set Environment Variables' - -inputs: - variablesPath: - description: 'The path to the YAML file that contains the key value pairs for environment variables' - required: true - -runs: - using: 'composite' - steps: - - # [Set environment variables] task(s) - # --------------- - - name: 'Set environment variables' - id: set-env-variables - shell: pwsh - run: | - # Grouping task logs - Write-Output '::group::Set environment variables' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'sharedScripts' 'Add-YamlListToFile.ps1') - - $functionInput = @{ - InputFilePath = '${{ inputs.variablesPath }}' - ListName = 'variables' - OutputFilePath = $Env:GITHUB_ENV - } - - Write-Verbose "Invoke task with" -Verbose - Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - - # Convert YAML Variable File to Environment Variables - Add-YamlListToFile @functionInput -Verbose - - Write-Output '::endgroup::' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 313a049..4a35b71 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,10 +24,18 @@ env: ARM_SUBSCRIPTION_ID: '${{ secrets.ARM_SUBSCRIPTION_ID }}' ARM_MGMTGROUP_ID: '${{ secrets.ARM_MGMTGROUP_ID }}' ARM_TENANT_ID: '${{ secrets.ARM_TENANT_ID }}' - TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' jobs: - job_publish_module: + job_pull_modules: + name: 'Publishing' + runs-on: ubuntu-20.04 + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + repository: 'Azure/ResourceModules' + fetch-depth: 0 + job_publish_modules: name: 'Publishing' if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.foo == 'true' runs-on: ubuntu-20.04 @@ -36,19 +44,3 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/modules/Microsoft.AAD/DomainServices/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.AAD/DomainServices/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..e1c6b97 --- /dev/null +++ b/modules/Microsoft.AAD/DomainServices/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,85 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource AzureADDS 'Microsoft.AAD/DomainServices@2021-05-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(AzureADDS.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: AzureADDS +}] diff --git a/modules/Microsoft.AAD/DomainServices/.test/parameters.json b/modules/Microsoft.AAD/DomainServices/.test/parameters.json new file mode 100644 index 0000000..6166d32 --- /dev/null +++ b/modules/Microsoft.AAD/DomainServices/.test/parameters.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "domainName": { + "value": "<>.onmicrosoft.com" + }, + "sku": { + "value": "Standard" + }, + "lock": { + "value": "CanNotDelete" + }, + "replicaSets": { + "value": [ + { + "location": "WestEurope", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-aadds-001/subnets/AADDSSubnet" + } + ] + }, + "pfxCertificate": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "pfxBase64Certificate" + } + }, + "pfxCertificatePassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "pfxCertificatePassword" + } + }, + "additionalRecipients": { + "value": [ + "<>@noreply.github.com" + ] + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.AAD/DomainServices/deploy.bicep b/modules/Microsoft.AAD/DomainServices/deploy.bicep new file mode 100644 index 0000000..4a08cd5 --- /dev/null +++ b/modules/Microsoft.AAD/DomainServices/deploy.bicep @@ -0,0 +1,268 @@ +@description('Optional. The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service.') +param name string = domainName + +@description('Required. The domain name specific to the Azure ADDS service.') +param domainName string + +@description('Optional. The name of the SKU specific to Azure ADDS Services.') +@allowed([ + 'Standard' + 'Enterprise' + 'Premium' +]) +param sku string = 'Standard' + +@description('Optional. The location to deploy the Azure ADDS Services.') +param location string = resourceGroup().location + +@description('Optional. Additional replica set for the managed domain.') +param replicaSets array = [] + +@description('Conditional. The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file. Required if secure LDAP is enabled and must be valid more than 30 days.') +param pfxCertificate string = '' + +@description('Conditional. The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled.') +@secure() +param pfxCertificatePassword string = '' + +@description('Optional. The email recipient value to receive alerts.') +param additionalRecipients array = [] + +@description('Optional. The value is to provide domain configuration type.') +@allowed([ + 'FullySynced' + 'ResourceTrusting' +]) +param domainConfigurationType string = 'FullySynced' + +@description('Optional. The value is to synchronize scoped users and groups.') +param filteredSync string = 'Enabled' + +@description('Optional. The value is to enable clients making request using TLSv1.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param tlsV1 string = 'Enabled' + +@description('Optional. The value is to enable clients making request using NTLM v1.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param ntlmV1 string = 'Enabled' + +@description('Optional. The value is to enable synchronized users to use NTLM authentication.') +@allowed([ + 'Enabled' + 'Disabled' +]) +#disable-next-line secure-secrets-in-params // Not a secret +param syncNtlmPasswords string = 'Enabled' + +@description('Optional. The value is to enable on-premises users to authenticate against managed domain.') +@allowed([ + 'Enabled' + 'Disabled' +]) +#disable-next-line secure-secrets-in-params // Not a secret +param syncOnPremPasswords string = 'Enabled' + +@description('Optional. The value is to enable Kerberos requests that use RC4 encryption.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param kerberosRc4Encryption string = 'Enabled' + +@description('Optional. The value is to enable to provide a protected channel between the Kerberos client and the KDC.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param kerberosArmoring string = 'Enabled' + +@description('Optional. The value is to notify the DC Admins.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param notifyDcAdmins string = 'Enabled' + +@description('Optional. The value is to notify the Global Admins.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param notifyGlobalAdmins string = 'Enabled' + +@description('Optional. The value is to enable the Secure LDAP for external services of Azure ADDS Services.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param externalAccess string = 'Enabled' + +@description('Optional. A flag to determine whether or not Secure LDAP is enabled or disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param ldaps string = 'Enabled' + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. The name of logs that will be streamed.') +@allowed([ + 'SystemSecurity' + 'AccountManagement' + 'LogonLogoff' + 'ObjectAccess' + 'PolicyChange' + 'PrivilegeUse' + 'DetailTracking' + 'DirectoryServiceAccess' + 'AccountLogon' +]) +param logsToEnable array = [ + 'SystemSecurity' + 'AccountManagement' + 'LogonLogoff' + 'ObjectAccess' + 'PolicyChange' + 'PrivilegeUse' + 'DetailTracking' + 'DirectoryServiceAccess' + 'AccountLogon' +] + +var diagnosticsLogs = [for log in logsToEnable: { + category: log + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource domainService 'Microsoft.AAD/DomainServices@2021-05-01' = { + name: name + location: location + tags: tags + properties: { + domainName: domainName + domainConfigurationType: domainConfigurationType + filteredSync: filteredSync + notificationSettings: { + additionalRecipients: additionalRecipients + notifyDcAdmins: notifyDcAdmins + notifyGlobalAdmins: notifyGlobalAdmins + } + ldapsSettings: { + externalAccess: externalAccess + ldaps: ldaps + pfxCertificate: !empty(pfxCertificate) ? pfxCertificate : null + pfxCertificatePassword: !empty(pfxCertificatePassword) ? pfxCertificatePassword : null + } + replicaSets: replicaSets + domainSecuritySettings: { + tlsV1: tlsV1 + ntlmV1: ntlmV1 + syncNtlmPasswords: syncNtlmPasswords + syncOnPremPasswords: syncOnPremPasswords + kerberosRc4Encryption: kerberosRc4Encryption + kerberosArmoring: kerberosArmoring + } + sku: sku + } +} + +resource domainService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: '${domainService.name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: domainService +} + +resource domainService_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${domainService.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: domainService +} + +module domainService_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VNet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: domainService.id + } +}] + +@description('The domain name of the Azure Active Directory Domain Services(Azure ADDS).') +output name string = domainService.name + +@description('The name of the resource group the Azure Active Directory Domain Services(Azure ADDS) was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Azure Active Directory Domain Services(Azure ADDS).') +output resourceId string = domainService.id + +@description('The location the resource was deployed into.') +output location string = domainService.location diff --git a/modules/Microsoft.AAD/DomainServices/readme.md b/modules/Microsoft.AAD/DomainServices/readme.md new file mode 100644 index 0000000..45a51b9 --- /dev/null +++ b/modules/Microsoft.AAD/DomainServices/readme.md @@ -0,0 +1,328 @@ +# Azure Active Directory Domain Services `[Microsoft.AAD/DomainServices]` + +This template deploys Azure Active Directory Domain Services (AADDS). + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AAD/domainServices` | [2021-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.AAD/2021-05-01/domainServices) | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `domainName` | string | The domain name specific to the Azure ADDS service. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `pfxCertificate` | string | `''` | The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file. Required if secure LDAP is enabled and must be valid more than 30 days. | +| `pfxCertificatePassword` | secureString | `''` | The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalRecipients` | array | `[]` | | The email recipient value to receive alerts. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `domainConfigurationType` | string | `'FullySynced'` | `[FullySynced, ResourceTrusting]` | The value is to provide domain configuration type. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `externalAccess` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable the Secure LDAP for external services of Azure ADDS Services. | +| `filteredSync` | string | `'Enabled'` | | The value is to synchronize scoped users and groups. | +| `kerberosArmoring` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable to provide a protected channel between the Kerberos client and the KDC. | +| `kerberosRc4Encryption` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable Kerberos requests that use RC4 encryption. | +| `ldaps` | string | `'Enabled'` | `[Disabled, Enabled]` | A flag to determine whether or not Secure LDAP is enabled or disabled. | +| `location` | string | `[resourceGroup().location]` | | The location to deploy the Azure ADDS Services. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `logsToEnable` | array | `[AccountLogon, AccountManagement, DetailTracking, DirectoryServiceAccess, LogonLogoff, ObjectAccess, PolicyChange, PrivilegeUse, SystemSecurity]` | `[AccountLogon, AccountManagement, DetailTracking, DirectoryServiceAccess, LogonLogoff, ObjectAccess, PolicyChange, PrivilegeUse, SystemSecurity]` | The name of logs that will be streamed. | +| `name` | string | `[parameters('domainName')]` | | The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. | +| `notifyDcAdmins` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to notify the DC Admins. | +| `notifyGlobalAdmins` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to notify the Global Admins. | +| `ntlmV1` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable clients making request using NTLM v1. | +| `replicaSets` | array | `[]` | | Additional replica set for the managed domain. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'Standard'` | `[Enterprise, Premium, Standard]` | The name of the SKU specific to Azure ADDS Services. | +| `syncNtlmPasswords` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable synchronized users to use NTLM authentication. | +| `syncOnPremPasswords` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable on-premises users to authenticate against managed domain. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `tlsV1` | string | `'Enabled'` | `[Disabled, Enabled]` | The value is to enable clients making request using TLSv1. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +
+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Considerations + +- A network security group has to be created and assigned to the designated AADDS subnet before deploying this module + - The following inbound rules should be allowed on the network security group + | Name | Protocol | Source Port Range | Source Address Prefix | Destination Port Range | Destination Address Prefix | + | - | - | - | - | - | - | + | AllowSyncWithAzureAD | TCP | `*` | `AzureActiveDirectoryDomainServices` | `443` | `*` | + | AllowPSRemoting | TCP | `*` | `AzureActiveDirectoryDomainServices` | `5986` | `*` | +- Associating a route table to the AADDS subnet is not recommended +- The network used for AADDS must have its DNS Servers [configured](https://docs.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-configure-networking#configure-dns-servers-in-the-peered-virtual-network) (e.g. with IPs `10.0.1.4` & `10.0.1.5`) +- Your Azure Active Directory must have the 'Domain Controller Services' service principal registered. If that's not the case, you can register it by executing the command `New-AzADServicePrincipal -ApplicationId '2565bd9d-da50-47d4-8b85-4c97f669dc36'` with an eligible user. + +### Create self-signed certificate for secure LDAP +Follow the below PowerShell commands to get base64 encoded string of a self-signed certificate (with a `pfxCertificatePassword`) + +```PowerShell +$pfxCertificatePassword = ConvertTo-SecureString '<>' -AsPlainText -Force +$certInputObject = @{ + Subject = 'CN=*.<>' + DnsName = '*.<>' + CertStoreLocation = 'cert:\LocalMachine\My' + KeyExportPolicy = 'Exportable' + Provider = 'Microsoft Enhanced RSA and AES Cryptographic Provider' + NotAfter = (Get-Date).AddMonths(3) + HashAlgorithm = 'SHA256' +} +$rawCert = New-SelfSignedCertificate @certInputObject +Export-PfxCertificate -Cert ('Cert:\localmachine\my\' + $rawCert.Thumbprint) -FilePath "$home/aadds.pfx" -Password $pfxCertificatePassword -Force +$rawCertByteStream = Get-Content "$home/aadds.pfx" -AsByteStream +$pfxCertificate = [System.Convert]::ToBase64String($rawCertByteStream) +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The domain name of the Azure Active Directory Domain Services(Azure ADDS). | +| `resourceGroupName` | string | The name of the resource group the Azure Active Directory Domain Services(Azure ADDS) was created in. | +| `resourceId` | string | The resource ID of the Azure Active Directory Domain Services(Azure ADDS). | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module DomainServices './Microsoft.AAD/DomainServices/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DomainServices' + params: { + // Required parameters + domainName: '<>.onmicrosoft.com' + // Non-required parameters + additionalRecipients: [ + '<>@noreply.github.com' + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + pfxCertificate: kv1.getSecret('pfxBase64Certificate') + pfxCertificatePassword: kv1.getSecret('pfxCertificatePassword') + replicaSets: [ + { + location: 'WestEurope' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-aadds-001/subnets/AADDSSubnet' + } + ] + sku: 'Standard' + } +} +``` + +
+

+ +

+ +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 + "domainName": { + "value": "<>.onmicrosoft.com" + }, + // Non-required parameters + "additionalRecipients": { + "value": [ + "<>@noreply.github.com" + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "pfxCertificate": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "pfxBase64Certificate" + } + }, + "pfxCertificatePassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "pfxCertificatePassword" + } + }, + "replicaSets": { + "value": [ + { + "location": "WestEurope", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-aadds-001/subnets/AADDSSubnet" + } + ] + }, + "sku": { + "value": "Standard" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.AAD/DomainServices/version.json b/modules/Microsoft.AAD/DomainServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.AAD/DomainServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.AnalysisServices/servers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.AnalysisServices/servers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d8d6a2b --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 server 'Microsoft.AnalysisServices/servers@2017-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(server.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: server +}] diff --git a/modules/Microsoft.AnalysisServices/servers/.test/max.parameters.json b/modules/Microsoft.AnalysisServices/servers/.test/max.parameters.json new file mode 100644 index 0000000..54abf71 --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/.test/max.parameters.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azasweumax001" + }, + "lock": { + "value": "CanNotDelete" + }, + "skuName": { + "value": "S0" + }, + "skuCapacity": { + "value": 1 + }, + "firewallSettings": { + "value": { + "firewallRules": [ + { + "firewallRuleName": "AllowFromAll", + "rangeStart": "0.0.0.0", + "rangeEnd": "255.255.255.255" + } + ], + "enablePowerBIService": true + } + }, + "diagnosticLogsRetentionInDays": { + "value": 365 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "Engine", + "Service" + ] + }, + "diagnosticMetricsToEnable": { + "value": [ + "AllMetrics" + ] + } + } +} diff --git a/modules/Microsoft.AnalysisServices/servers/.test/min.parameters.json b/modules/Microsoft.AnalysisServices/servers/.test/min.parameters.json new file mode 100644 index 0000000..33c0605 --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/.test/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": "<>azasweumin001" + } + } +} diff --git a/modules/Microsoft.AnalysisServices/servers/.test/parameters.json b/modules/Microsoft.AnalysisServices/servers/.test/parameters.json new file mode 100644 index 0000000..592ffff --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/.test/parameters.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azasweux001" + }, + "lock": { + "value": "CanNotDelete" + }, + "skuName": { + "value": "S0" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.AnalysisServices/servers/deploy.bicep b/modules/Microsoft.AnalysisServices/servers/deploy.bicep new file mode 100644 index 0000000..e5ba3a6 --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/deploy.bicep @@ -0,0 +1,169 @@ +@description('Required. The name of the Azure Analysis Services server to create.') +param name string + +@description('Optional. The SKU name of the Azure Analysis Services server to create.') +param skuName string = 'S0' + +@description('Optional. The total number of query replica scale-out instances.') +param skuCapacity int = 1 + +@description('Optional. The inbound firewall rules to define on the server. If not specified, firewall is disabled.') +param firewallSettings object = { + firewallRules: [ + { + firewallRuleName: 'AllowFromAll' + rangeStart: '0.0.0.0' + rangeEnd: '255.255.255.255' + } + ] + enablePowerBIService: true +} + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'Engine' + 'Service' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Engine' + 'Service' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.AnalysisServices/servers@2017-08-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + capacity: skuCapacity + } + properties: { + ipV4FirewallSettings: firewallSettings + } +} + +resource server_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${server.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: server +} + +resource server_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: server +} + +module server_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AnServicesServer-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: server.id + } +}] + +@description('The name of the analysis service.') +output name string = server.name + +@description('The resource ID of the analysis service.') +output resourceId string = server.id + +@description('The resource group the analysis service was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = server.location diff --git a/modules/Microsoft.AnalysisServices/servers/readme.md b/modules/Microsoft.AnalysisServices/servers/readme.md new file mode 100644 index 0000000..92765a4 --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/readme.md @@ -0,0 +1,423 @@ +# Analysis Services Servers `[Microsoft.AnalysisServices/servers]` + +This module deploys an Analysis Services Server. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AnalysisServices/servers` | [2017-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.AnalysisServices/2017-08-01/servers) | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Azure Analysis Services server to create. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Engine, Service]` | `[Engine, Service]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `firewallSettings` | object | `{object}` | | The inbound firewall rules to define on the server. If not specified, firewall is disabled. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `skuCapacity` | int | `1` | | The total number of query replica scale-out instances. | +| `skuName` | string | `'S0'` | | The SKU name of the Azure Analysis Services server to create. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the analysis service. | +| `resourceGroupName` | string | The resource group the analysis service was deployed into. | +| `resourceId` | string | The resource ID of the analysis service. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Max

+ +
+ +via Bicep module + +```bicep +module servers './Microsoft.AnalysisServices/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + // Required parameters + name: '<>azasweumax001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogCategoriesToEnable: [ + 'Engine' + 'Service' + ] + diagnosticLogsRetentionInDays: 365 + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + firewallSettings: { + enablePowerBIService: true + firewallRules: [ + { + firewallRuleName: 'AllowFromAll' + rangeEnd: '255.255.255.255' + rangeStart: '0.0.0.0' + } + ] + } + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuCapacity: 1 + skuName: 'S0' + } +} +``` + +
+

+ +

+ +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": "<>azasweumax001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "Engine", + "Service" + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 365 + }, + "diagnosticMetricsToEnable": { + "value": [ + "AllMetrics" + ] + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "firewallSettings": { + "value": { + "enablePowerBIService": true, + "firewallRules": [ + { + "firewallRuleName": "AllowFromAll", + "rangeEnd": "255.255.255.255", + "rangeStart": "0.0.0.0" + } + ] + } + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuCapacity": { + "value": 1 + }, + "skuName": { + "value": "S0" + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module servers './Microsoft.AnalysisServices/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + name: '<>azasweumin001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azasweumin001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module servers './Microsoft.AnalysisServices/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + // Required parameters + name: '<>azasweux001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'S0' + } +} +``` + +
+

+ +

+ +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": "<>azasweux001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuName": { + "value": "S0" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.AnalysisServices/servers/version.json b/modules/Microsoft.AnalysisServices/servers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.AnalysisServices/servers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/.bicep/nested_authorizationServers.bicep b/modules/Microsoft.ApiManagement/service/.bicep/nested_authorizationServers.bicep new file mode 100644 index 0000000..5201c73 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/.bicep/nested_authorizationServers.bicep @@ -0,0 +1,117 @@ +@description('Required. Identifier of the authorization server.') +param name string + +@description('Required. The name of the API Management service.') +param apiManagementServiceName string + +@description('Required. OAuth authorization endpoint. See .') +param authorizationEndpoint string + +@description('Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE') +param authorizationMethods array = [ + 'GET' +] + +@description('Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query') +param bearerTokenSendingMethods array = [ + 'authorizationHeader' +] + +@description('Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body') +param clientAuthenticationMethod array = [ + 'Basic' +] + +@description('Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced.') +param clientRegistrationEndpoint string = '' + +@description('Required. Name of the key vault that stores clientId and clientSecret for this authorization server.') +param clientCredentialsKeyVaultId string + +@description('Required. Name of the secret that stores the Client or app ID registered with this authorization server.') +param clientIdSecretName string + +@description('Required. Name of the secret that stores the Client or app secret registered with this authorization server. This property will not be filled on \'GET\' operations! Use \'/listSecrets\' POST request to get the value.') +param clientSecretSecretName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values.') +param defaultScope string = '' + +@description('Optional. Description of the authorization server. Can contain HTML formatting tags.') +param serverDescription string = '' + +@description('Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials') +param grantTypes array + +@description('Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password.') +#disable-next-line secure-secrets-in-params // Not a secret +param resourceOwnerPassword string = '' + +@description('Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username.') +param resourceOwnerUsername string = '' + +@description('Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security.') +param supportState bool = false + +@description('Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties, i.e. {"name" : "name value", "value": "a value"}. - TokenBodyParameterContract object') +param tokenBodyParameters array = [] + +@description('Optional. OAuth token endpoint. Contains absolute URI to entity being referenced.') +param tokenEndpoint string = '' + +var defaultAuthorizationMethods = [ + 'GET' +] +var setAuthorizationMethods = union(authorizationMethods, defaultAuthorizationMethods) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: last(split(clientCredentialsKeyVaultId, '/')) + scope: resourceGroup(split(clientCredentialsKeyVaultId, '/')[2], split(clientCredentialsKeyVaultId, '/')[4]) +} + +module authorizationServer '../authorizationServers/deploy.bicep' = { + name: '${deployment().name}-AuthorizationServer' + params: { + apiManagementServiceName: apiManagementServiceName + serverDescription: serverDescription + authorizationMethods: setAuthorizationMethods + clientAuthenticationMethod: clientAuthenticationMethod + tokenBodyParameters: tokenBodyParameters + tokenEndpoint: tokenEndpoint + supportState: supportState + defaultScope: defaultScope + bearerTokenSendingMethods: bearerTokenSendingMethods + resourceOwnerUsername: resourceOwnerUsername + resourceOwnerPassword: resourceOwnerPassword + name: name + clientRegistrationEndpoint: clientRegistrationEndpoint + authorizationEndpoint: authorizationEndpoint + grantTypes: grantTypes + clientId: keyVault.getSecret(clientIdSecretName) + clientSecret: keyVault.getSecret(clientSecretSecretName) + } +} + +@description('The name of the API management service authorization server') +output name string = authorizationServer.outputs.name + +@description('The resource ID of the API management service authorization server') +output resourceId string = authorizationServer.outputs.resourceId + +@description('The resource group the API management service authorization server was deployed into') +output resourceGroupName string = authorizationServer.outputs.resourceGroupName diff --git a/modules/Microsoft.ApiManagement/service/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ApiManagement/service/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..630bfac --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + '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 service 'Microsoft.ApiManagement/service@2020-12-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(service.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: service +}] diff --git a/modules/Microsoft.ApiManagement/service/.test/max.parameters.json b/modules/Microsoft.ApiManagement/service/.test/max.parameters.json new file mode 100644 index 0000000..f760ecf --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/.test/max.parameters.json @@ -0,0 +1,177 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-apim-max-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + }, + "apis": { + "value": [ + { + "name": "echo-api", + "displayName": "Echo API", + "path": "echo", + "serviceUrl": "http://echoapi.cloudapp.net/api", + "apiVersionSet": { + "name": "echo-version-set", + "properties": { + "description": "echo-version-set", + "displayName": "echo-version-set", + "versioningScheme": "Segment" + } + } + } + ] + }, + "authorizationServers": { + "value": [ + { + "name": "AuthServer1", + "authorizationEndpoint": "https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/authorize", + "grantTypes": [ + "authorizationCode" + ], + "clientCredentialsKeyVaultId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "clientIdSecretName": "apimclientid", + "clientSecretSecretName": "apimclientsecret", + "clientRegistrationEndpoint": "http://localhost", + "tokenEndpoint": "https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token" + } + ] + }, + "backends": { + "value": [ + { + "name": "backend", + "url": "http://echoapi.cloudapp.net/api", + "tls": { + "validateCertificateChain": false, + "validateCertificateName": false + } + } + ] + }, + "caches": { + "value": [ + { + "name": "westeurope", + "connectionString": "connectionstringtest", + "useFromLocation": "westeurope" + } + ] + }, + "identityProviders": { + "value": [ + { + "name": "aadProvider" + } + ] + }, + "namedValues": { + "value": [ + { + "name": "apimkey", + "displayName": "apimkey", + "secret": true + } + ] + }, + "policies": { + "value": [ + { + "value": " ", + "format": "xml" + } + ] + }, + "portalSettings": { + "value": [ + { + "name": "signin", + "properties": { + "enabled": false + } + }, + { + "name": "signup", + "properties": { + "enabled": false, + "termsOfService": { + "enabled": false, + "consentRequired": false + } + } + } + ] + }, + "products": { + "value": [ + { + "name": "Starter", + "subscriptionRequired": false, + "approvalRequired": false, + "apis": [ + { + "name": "echo-api" + } + ], + "groups": [ + { + "name": "developers" + } + ] + } + ] + }, + "subscriptions": { + "value": [ + { + "scope": "/apis", + "name": "testArmSubscriptionAllApis" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.ApiManagement/service/.test/min.parameters.json b/modules/Microsoft.ApiManagement/service/.test/min.parameters.json new file mode 100644 index 0000000..d71e822 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/.test/min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-apim-min-001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + } + } +} diff --git a/modules/Microsoft.ApiManagement/service/.test/parameters.json b/modules/Microsoft.ApiManagement/service/.test/parameters.json new file mode 100644 index 0000000..8f73097 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/.test/parameters.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-apim-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + }, + "portalSettings": { + "value": [ + { + "name": "signin", + "properties": { + "enabled": false + } + }, + { + "name": "signup", + "properties": { + "enabled": false, + "termsOfService": { + "enabled": false, + "consentRequired": false + } + } + } + ] + }, + "policies": { + "value": [ + { + "value": " ", + "format": "xml" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.ApiManagement/service/apiVersionSets/deploy.bicep b/modules/Microsoft.ApiManagement/service/apiVersionSets/deploy.bicep new file mode 100644 index 0000000..bec9d25 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apiVersionSets/deploy.bicep @@ -0,0 +1,42 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. API Version set name.') +param name string = 'default' + +@description('Optional. API Version set properties.') +param properties object = {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource apiVersionSet 'Microsoft.ApiManagement/service/apiVersionSets@2021-08-01' = { + name: name + parent: service + properties: properties +} + +@description('The resource ID of the API Version set.') +output resourceId string = apiVersionSet.id + +@description('The name of the API Version set.') +output name string = apiVersionSet.name + +@description('The resource group the API Version set was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/apiVersionSets/readme.md b/modules/Microsoft.ApiManagement/service/apiVersionSets/readme.md new file mode 100644 index 0000000..e4e307b --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apiVersionSets/readme.md @@ -0,0 +1,43 @@ +# API Management Service API Version Sets `[Microsoft.ApiManagement/service/apiVersionSets]` + +This module deploys API Management Service APIs Version Set. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/apiVersionSets` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apiVersionSets) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | API Version set name. | +| `properties` | object | `{object}` | API Version set properties. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API Version set. | +| `resourceGroupName` | string | The resource group the API Version set was deployed into. | +| `resourceId` | string | The resource ID of the API Version set. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/apiVersionSets/version.json b/modules/Microsoft.ApiManagement/service/apiVersionSets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apiVersionSets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/apis/deploy.bicep b/modules/Microsoft.ApiManagement/service/apis/deploy.bicep new file mode 100644 index 0000000..067e173 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/deploy.bicep @@ -0,0 +1,160 @@ +@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 name string + +@description('Optional. Array of Policies to apply to the Service API.') +param policies array = [] + +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Describes the Revision of the API. If no value is provided, default revision 1 is created.') +param apiRevision string = '' + +@description('Optional. Description of the API Revision.') +param apiRevisionDescription string = '' + +@description('Optional. Type of API to create. * http creates a SOAP to REST API * soap creates a SOAP pass-through API.') +@allowed([ + 'http' + 'soap' +]) +param apiType string = 'http' + +@description('Optional. Indicates the Version identifier of the API if the API is versioned.') +param apiVersion string = '' + +@description('Optional. Indicates the Version identifier of the API version set.') +param apiVersionSetId string = '' + +@description('Optional. Description of the API Version.') +param apiVersionDescription string = '' + +@description('Optional. Collection of authentication settings included into this API.') +param authenticationSettings object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Description of the API. May include HTML formatting tags.') +param apiDescription string = '' + +@description('Required. API name. Must be 1 to 300 characters long.') +@maxLength(300) +param displayName string + +@description('Optional. Format of the Content in which the API is getting imported.') +@allowed([ + 'wadl-xml' + 'wadl-link-json' + 'swagger-json' + 'swagger-link-json' + 'wsdl' + 'wsdl-link' + 'openapi' + 'openapi+json' + 'openapi-link' + 'openapi+json-link' +]) +param format string = 'openapi' + +@description('Optional. Indicates if API revision is current API revision.') +param isCurrent bool = true + +@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 path string + +@description('Optional. Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS.') +param protocols array = [ + 'https' +] + +@description('Optional. Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters long.') +@maxLength(2000) +param serviceUrl string = '' + +@description('Optional. API identifier of the source API.') +param sourceApiId string = '' + +@description('Optional. Protocols over which API is made available.') +param subscriptionKeyParameterNames object = {} + +@description('Optional. Specifies whether an API or Product subscription is required for accessing the API.') +param subscriptionRequired bool = false + +@description('Optional. Type of API.') +@allowed([ + 'http' + 'soap' +]) +param type string = 'http' + +@description('Optional. Content value when Importing an API.') +param value string = '' + +@description('Optional. Criteria to limit import of WSDL to a subset of the document.') +param wsdlSelector object = {} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource api 'Microsoft.ApiManagement/service/apis@2021-08-01' = { + name: name + parent: service + properties: { + apiRevision: !empty(apiRevision) ? apiRevision : null + apiRevisionDescription: !empty(apiRevisionDescription) ? apiRevisionDescription : null + apiType: !empty(apiType) ? apiType : null + apiVersion: !empty(apiVersion) ? apiVersion : null + apiVersionDescription: !empty(apiVersionDescription) ? apiVersionDescription : null + apiVersionSetId: !empty(apiVersionSetId) ? apiVersionSetId : null + authenticationSettings: authenticationSettings + description: apiDescription + displayName: displayName + format: !empty(value) ? format : null + isCurrent: isCurrent + path: path + protocols: protocols + serviceUrl: !empty(serviceUrl) ? serviceUrl : null + sourceApiId: !empty(sourceApiId) ? sourceApiId : null + subscriptionKeyParameterNames: !empty(subscriptionKeyParameterNames) ? subscriptionKeyParameterNames : null + subscriptionRequired: subscriptionRequired + type: type + value: !empty(value) ? value : null + wsdlSelector: wsdlSelector + } +} + +module policy 'policies/deploy.bicep' = [for (policy, index) in policies: { + name: '${deployment().name}-Policy-${index}' + params: { + apiManagementServiceName: apiManagementServiceName + apiName: api.name + format: contains(policy, 'format') ? policy.format : 'xml' + value: policy.value + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the API management service API.') +output name string = api.name + +@description('The resource ID of the API management service API.') +output resourceId string = api.id + +@description('The resource group the API management service API was deployed to.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/apis/policies/deploy.bicep b/modules/Microsoft.ApiManagement/service/apis/policies/deploy.bicep new file mode 100644 index 0000000..88efbdf --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/policies/deploy.bicep @@ -0,0 +1,61 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Conditional. The name of the parent API. Required if the template is used in a standalone deployment.') +param apiName string + +@description('Optional. The name of the policy.') +param name string = 'policy' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Format of the policyContent.') +@allowed([ + 'rawxml' + 'rawxml-link' + 'xml' + 'xml-link' +]) +param format string = 'xml' + +@description('Required. Contents of the Policy as defined by the format.') +param value string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName + + resource api 'apis@2021-08-01' existing = { + name: apiName + } +} + +resource policy 'Microsoft.ApiManagement/service/apis/policies@2021-08-01' = { + name: name + parent: service::api + properties: { + format: format + value: value + } +} + +@description('The resource ID of the API policy.') +output resourceId string = policy.id + +@description('The name of the API policy.') +output name string = policy.name + +@description('The resource group the API policy was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/apis/policies/readme.md b/modules/Microsoft.ApiManagement/service/apis/policies/readme.md new file mode 100644 index 0000000..54683b2 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/policies/readme.md @@ -0,0 +1,49 @@ +# API Management Service APIs Policies `[Microsoft.ApiManagement/service/apis/policies]` + +This module deploys API Management Service APIs policies. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/apis/policies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apis/policies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `value` | string | Contents of the Policy as defined by the format. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | +| `apiName` | string | The name of the parent API. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `format` | string | `'xml'` | `[rawxml, rawxml-link, xml, xml-link]` | Format of the policyContent. | +| `name` | string | `'policy'` | | The name of the policy. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API policy. | +| `resourceGroupName` | string | The resource group the API policy was deployed into. | +| `resourceId` | string | The resource ID of the API policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/apis/policies/version.json b/modules/Microsoft.ApiManagement/service/apis/policies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/policies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/apis/readme.md b/modules/Microsoft.ApiManagement/service/apis/readme.md new file mode 100644 index 0000000..a579195 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/readme.md @@ -0,0 +1,68 @@ +# API Management Service APIs `[Microsoft.ApiManagement/service/apis]` + +This module deploys API Management Service APIs. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/apis` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apis) | +| `Microsoft.ApiManagement/service/apis/policies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apis/policies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `displayName` | string | API name. Must be 1 to 300 characters long. | +| `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` | 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 Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `apiDescription` | string | `''` | | Description of the API. May include HTML formatting tags. | +| `apiRevision` | string | `''` | | Describes the Revision of the API. If no value is provided, default revision 1 is created. | +| `apiRevisionDescription` | string | `''` | | Description of the API Revision. | +| `apiType` | string | `'http'` | `[http, soap]` | Type of API to create. * http creates a SOAP to REST API * soap creates a SOAP pass-through API. | +| `apiVersion` | string | `''` | | Indicates the Version identifier of the API if the API is versioned. | +| `apiVersionDescription` | string | `''` | | Description of the API Version. | +| `apiVersionSetId` | string | `''` | | Indicates the Version identifier of the API version set. | +| `authenticationSettings` | object | `{object}` | | Collection of authentication settings included into this API. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `format` | string | `'openapi'` | `[openapi, openapi-link, openapi+json, openapi+json-link, swagger-json, swagger-link-json, wadl-link-json, wadl-xml, wsdl, wsdl-link]` | Format of the Content in which the API is getting imported. | +| `isCurrent` | bool | `True` | | Indicates if API revision is current API revision. | +| `policies` | _[policies](policies/readme.md)_ array | `[]` | | Array of Policies to apply to the Service API. | +| `protocols` | array | `[https]` | | Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS. | +| `serviceUrl` | string | `''` | | Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters long. | +| `sourceApiId` | string | `''` | | API identifier of the source API. | +| `subscriptionKeyParameterNames` | object | `{object}` | | Protocols over which API is made available. | +| `subscriptionRequired` | bool | `False` | | Specifies whether an API or Product subscription is required for accessing the API. | +| `type` | string | `'http'` | `[http, soap]` | Type of API. | +| `value` | string | `''` | | Content value when Importing an API. | +| `wsdlSelector` | object | `{object}` | | Criteria to limit import of WSDL to a subset of the document. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service API. | +| `resourceGroupName` | string | The resource group the API management service API was deployed to. | +| `resourceId` | string | The resource ID of the API management service API. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/apis/version.json b/modules/Microsoft.ApiManagement/service/apis/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/apis/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/authorizationServers/deploy.bicep b/modules/Microsoft.ApiManagement/service/authorizationServers/deploy.bicep new file mode 100644 index 0000000..e487b7e --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/authorizationServers/deploy.bicep @@ -0,0 +1,115 @@ +@description('Required. Identifier of the authorization server.') +param name string + +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Required. OAuth authorization endpoint. See .') +param authorizationEndpoint string + +@description('Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE.') +param authorizationMethods array = [ + 'GET' +] + +@description('Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query.') +param bearerTokenSendingMethods array = [ + 'authorizationHeader' +] + +@description('Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body.') +param clientAuthenticationMethod array = [ + 'Basic' +] + +@description('Required. Client or app ID registered with this authorization server.') +@secure() +param clientId string + +@description('Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced.') +param clientRegistrationEndpoint string = '' + +@description('Required. Client or app secret registered with this authorization server. This property will not be filled on \'GET\' operations! Use \'/listSecrets\' POST request to get the value.') +@secure() +param clientSecret string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values.') +param defaultScope string = '' + +@description('Optional. Description of the authorization server. Can contain HTML formatting tags.') +param serverDescription string = '' + +@description('Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials.') +param grantTypes array + +@description('Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password.') +#disable-next-line secure-secrets-in-params // Not a secret +param resourceOwnerPassword string = '' + +@description('Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username.') +param resourceOwnerUsername string = '' + +@description('Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security.') +param supportState bool = false + +@description('Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties, i.e. {"name" : "name value", "value": "a value"}. - TokenBodyParameterContract object.') +param tokenBodyParameters array = [] + +@description('Optional. OAuth token endpoint. Contains absolute URI to entity being referenced.') +param tokenEndpoint string = '' + +var defaultAuthorizationMethods = [ + 'GET' +] +var setAuthorizationMethods = union(authorizationMethods, defaultAuthorizationMethods) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource authorizationServer 'Microsoft.ApiManagement/service/authorizationServers@2021-08-01' = { + name: name + parent: service + properties: { + description: serverDescription + authorizationMethods: setAuthorizationMethods + clientAuthenticationMethod: clientAuthenticationMethod + tokenBodyParameters: tokenBodyParameters + tokenEndpoint: tokenEndpoint + supportState: supportState + defaultScope: defaultScope + bearerTokenSendingMethods: bearerTokenSendingMethods + resourceOwnerUsername: resourceOwnerUsername + resourceOwnerPassword: resourceOwnerPassword + displayName: name + clientRegistrationEndpoint: clientRegistrationEndpoint + authorizationEndpoint: authorizationEndpoint + grantTypes: grantTypes + clientId: clientId + clientSecret: clientSecret + } +} + +@description('The name of the API management service authorization server.') +output name string = authorizationServer.name + +@description('The resource ID of the API management service authorization server.') +output resourceId string = authorizationServer.id + +@description('The resource group the API management service authorization server was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/authorizationServers/readme.md b/modules/Microsoft.ApiManagement/service/authorizationServers/readme.md new file mode 100644 index 0000000..31b4c90 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/authorizationServers/readme.md @@ -0,0 +1,67 @@ +# API Management Service Authorization Servers `[Microsoft.ApiManagement/service/authorizationServers]` + +This module deploys API Management Service Authorization Servers. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/authorizationServers` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/authorizationServers) | + +### Resource dependency + +The following resources are required to be able to deploy this resource. + +- `Microsoft.ApiManagement/service` + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `authorizationEndpoint` | string | OAuth authorization endpoint. See . | +| `clientId` | secureString | Client or app ID registered with this authorization server. | +| `clientSecret` | secureString | Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value. | +| `grantTypes` | array | Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials. | +| `name` | string | Identifier of the authorization server. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `authorizationMethods` | array | `[GET]` | HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE. | +| `bearerTokenSendingMethods` | array | `[authorizationHeader]` | Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query. | +| `clientAuthenticationMethod` | array | `[Basic]` | Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body. | +| `clientRegistrationEndpoint` | string | `''` | Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced. | +| `defaultScope` | string | `''` | Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `resourceOwnerPassword` | string | `''` | Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password. | +| `resourceOwnerUsername` | string | `''` | Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username. | +| `serverDescription` | string | `''` | Description of the authorization server. Can contain HTML formatting tags. | +| `supportState` | bool | `False` | If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security. | +| `tokenBodyParameters` | array | `[]` | Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties, i.e. {"name" : "name value", "value": "a value"}. - TokenBodyParameterContract object. | +| `tokenEndpoint` | string | `''` | OAuth token endpoint. Contains absolute URI to entity being referenced. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service authorization server. | +| `resourceGroupName` | string | The resource group the API management service authorization server was deployed into. | +| `resourceId` | string | The resource ID of the API management service authorization server. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/authorizationServers/version.json b/modules/Microsoft.ApiManagement/service/authorizationServers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/authorizationServers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/backends/deploy.bicep b/modules/Microsoft.ApiManagement/service/backends/deploy.bicep new file mode 100644 index 0000000..e5b0c40 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/backends/deploy.bicep @@ -0,0 +1,81 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Required. Backend Name.') +param name string + +@description('Optional. Backend Credentials Contract Properties.') +param credentials object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Backend Description.') +param backendDescription string = '' + +@description('Optional. Backend communication protocol. - http or soap.') +param protocol string = 'http' + +@description('Optional. Backend Proxy Contract Properties.') +param proxy object = {} + +@description('Optional. Management Uri of the Resource in External System. This URL can be the Arm Resource ID of Logic Apps, Function Apps or API Apps.') +param resourceId string = '' + +@description('Optional. Backend Service Fabric Cluster Properties.') +param serviceFabricCluster object = {} + +@description('Optional. Backend Title.') +param title string = '' + +@description('Optional. Backend TLS Properties.') +param tls object = { + validateCertificateChain: false + validateCertificateName: false +} + +@description('Required. Runtime URL of the Backend.') +param url string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource backend 'Microsoft.ApiManagement/service/backends@2021-08-01' = { + name: name + parent: service + properties: { + title: !empty(title) ? title : null + description: !empty(backendDescription) ? backendDescription : null + resourceId: !empty(resourceId) ? resourceId : null + properties: { + serviceFabricCluster: !empty(serviceFabricCluster) ? serviceFabricCluster : null + } + credentials: !empty(credentials) ? credentials : null + proxy: !empty(proxy) ? proxy : null + tls: !empty(tls) ? tls : null + url: url + protocol: protocol + } +} + +@description('The resource ID of the API management service backend.') +output resourceId string = backend.id + +@description('The name of the API management service backend.') +output name string = backend.name + +@description('The resource group the API management service backend was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/backends/readme.md b/modules/Microsoft.ApiManagement/service/backends/readme.md new file mode 100644 index 0000000..82877f3 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/backends/readme.md @@ -0,0 +1,146 @@ +# API Management Service Backends `[Microsoft.ApiManagement/service/backends]` + +This module deploys API Management Service Backends. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/backends` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/backends) | + +### Resource dependency + +The following resources are required to be able to deploy this resource. + +- `Microsoft.ApiManagement/service` + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Backend Name. | +| `url` | string | Runtime URL of the Backend. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `backendDescription` | string | `''` | Backend Description. | +| `credentials` | object | `{object}` | Backend Credentials Contract Properties. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `protocol` | string | `'http'` | Backend communication protocol. - http or soap. | +| `proxy` | object | `{object}` | Backend Proxy Contract Properties. | +| `resourceId` | string | `''` | Management Uri of the Resource in External System. This URL can be the Arm Resource ID of Logic Apps, Function Apps or API Apps. | +| `serviceFabricCluster` | object | `{object}` | Backend Service Fabric Cluster Properties. | +| `title` | string | `''` | Backend Title. | +| `tls` | object | `{object}` | Backend TLS Properties. | + + +### Parameter Usage: Credentials + +| Parameter Name| Type | Default Value | Possible values | Description | +| :-- | :-- | :--- | :-- | :- | +| `certificate` | array | | | Optional. List of Client Certificate Thumbprint. - string | +| `query` | object | | | Optional. Query Parameter description. | +| `header` | object | | | Optional. Header Parameter description. | +| `authorization` | object | | | Optional. Authorization header authentication | + +### Parameter Usage: `credentials` + +

+ +Parameter JSON format + +```json +"credentials": { + "value":{ + "certificate": [ + "string" + ], + "query": {}, + "header": {}, + "authorization": { + "scheme": "Authentication Scheme name.-string", + "parameter": "Authentication Parameter value. - string" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +credentials: { + certificate: [ + 'string' + ] + query: {} + header: {} + authorization: { + scheme: 'Authentication Scheme name.-string' + parameter: 'Authentication Parameter value. - string' + } +} +``` + +
+

+ +### Parameter Usage: `tls` + +

+ +Parameter JSON format + +```json +"tls": { + "value":{ + "validateCertificateChain": "Flag indicating whether SSL certificate chain validation should be done when using self-signed certificates for this backend host. - boolean", + "validateCertificateName": "Flag indicating whether SSL certificate name validation should be done when using self-signed certificates for this backend host. - boolean" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tls: { + validateCertificateChain: 'Flag indicating whether SSL certificate chain validation should be done when using self-signed certificates for this backend host. - boolean' + validateCertificateName: 'Flag indicating whether SSL certificate name validation should be done when using self-signed certificates for this backend host. - boolean' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service backend. | +| `resourceGroupName` | string | The resource group the API management service backend was deployed into. | +| `resourceId` | string | The resource ID of the API management service backend. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/backends/version.json b/modules/Microsoft.ApiManagement/service/backends/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/backends/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/caches/deploy.bicep b/modules/Microsoft.ApiManagement/service/caches/deploy.bicep new file mode 100644 index 0000000..2cb7c6b --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/caches/deploy.bicep @@ -0,0 +1,56 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Required. Identifier of the Cache entity. Cache identifier (should be either \'default\' or valid Azure region identifier).') +param name string + +@description('Required. Runtime connection string to cache. Can be referenced by a named value like so, {{}}.') +param connectionString string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Cache description.') +param cacheDescription string = '' + +@description('Optional. Original uri of entity in external system cache points to.') +param resourceId string = '' + +@description('Required. Location identifier to use cache from (should be either \'default\' or valid Azure region identifier).') +param useFromLocation string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource cache 'Microsoft.ApiManagement/service/caches@2021-08-01' = { + name: name + parent: service + properties: { + description: !empty(cacheDescription) ? cacheDescription : null + connectionString: connectionString + useFromLocation: useFromLocation + resourceId: !empty(resourceId) ? resourceId : null + } +} + +@description('The resource ID of the API management service cache.') +output resourceId string = cache.id + +@description('The name of the API management service cache.') +output name string = cache.name + +@description('The resource group the API management service cache was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/caches/readme.md b/modules/Microsoft.ApiManagement/service/caches/readme.md new file mode 100644 index 0000000..2074610 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/caches/readme.md @@ -0,0 +1,50 @@ +# API Management Service Cache `[Microsoft.ApiManagement/service/caches]` + +This module deploys an API Management Service Cache. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/caches` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/caches) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `connectionString` | string | Runtime connection string to cache. Can be referenced by a named value like so, {{}}. | +| `name` | string | Identifier of the Cache entity. Cache identifier (should be either 'default' or valid Azure region identifier). | +| `useFromLocation` | string | Location identifier to use cache from (should be either 'default' or valid Azure region identifier). | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cacheDescription` | string | `''` | Cache description. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `resourceId` | string | `''` | Original uri of entity in external system cache points to. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service cache. | +| `resourceGroupName` | string | The resource group the API management service cache was deployed into. | +| `resourceId` | string | The resource ID of the API management service cache. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/caches/version.json b/modules/Microsoft.ApiManagement/service/caches/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/caches/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/deploy.bicep b/modules/Microsoft.ApiManagement/service/deploy.bicep new file mode 100644 index 0000000..ff8b436 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/deploy.bicep @@ -0,0 +1,470 @@ +@description('Optional. Additional datacenter locations of the API Management service.') +param additionalLocations array = [] + +@description('Required. The name of the API Management service.') +param name string + +@description('Optional. List of Certificates that need to be installed in the API Management service. Max supported certificates that can be installed is 10.') +@maxLength(10) +param certificates array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Custom properties of the API Management service.') +param customProperties object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Property only valid for an API Management service deployed in multiple locations. This can be used to disable the gateway in master region.') +param disableGateway bool = false + +@description('Optional. Property only meant to be used for Consumption SKU Service. This enforces a client certificate to be presented on each request to the gateway. This also enables the ability to authenticate the certificate in the policy on the gateway.') +param enableClientCertificate bool = false + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Custom hostname configuration of the API Management service.') +param hostnameConfigurations array = [] + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Limit control plane API calls to API Management service with version equal to or newer than this value.') +param minApiVersion string = '' + +@description('Optional. The notification sender email address for the service.') +param notificationSenderEmail string = 'apimgmt-noreply@mail.windowsazure.com' + +@description('Required. The email address of the owner of the service.') +param publisherEmail string + +@description('Required. The name of the owner of the service.') +param publisherName string + +@description('Optional. 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.') +param restore bool = false + +@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. The pricing tier of this API Management service.') +@allowed([ + 'Consumption' + 'Developer' + 'Basic' + 'Standard' + 'Premium' +]) +param sku string = 'Developer' + +@description('Optional. The instance size of this API Management service.') +@allowed([ + 1 + 2 +]) +param skuCount int = 1 + +@description('Optional. The full resource ID of a subnet in a virtual network to deploy the API Management service in.') +param subnetResourceId string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The type of VPN in which API Management service needs to be configured in. None (Default Value) means the API Management service is not part of any Virtual Network, External means the API Management deployment is set up inside a Virtual Network having an internet Facing Endpoint, and Internal means that API Management deployment is setup inside a Virtual Network having an Intranet Facing Endpoint only.') +@allowed([ + 'None' + 'External' + 'Internal' +]) +param virtualNetworkType string = 'None' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. A list of availability zones denoting where the resource needs to come from.') +param zones array = [] + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'GatewayLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'GatewayLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] +@description('Optional. Necessary to create a new GUID.') +param newGuidValue string = newGuid() + +@description('Optional. APIs.') +param apis array = [] +@description('Optional. API Version Sets.') +param apiVersionSets array = [] +@description('Optional. Authorization servers.') +param authorizationServers array = [] +@description('Optional. Backends.') +param backends array = [] +@description('Optional. Caches.') +param caches array = [] +@description('Optional. Identity providers.') +param identityProviders array = [] +@description('Optional. Named values.') +param namedValues array = [] +@description('Optional. Policies.') +param policies array = [] +@description('Optional. Portal settings.') +param portalSettings array = [] +@description('Optional. Products.') +param products array = [] +@description('Optional. Subscriptions.') +param subscriptions array = [] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource apiManagementService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: sku + capacity: skuCount + } + zones: zones + identity: identity + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + notificationSenderEmail: notificationSenderEmail + hostnameConfigurations: hostnameConfigurations + additionalLocations: additionalLocations + customProperties: customProperties + certificates: certificates + enableClientCertificate: enableClientCertificate ? true : null + disableGateway: disableGateway + virtualNetworkType: virtualNetworkType + virtualNetworkConfiguration: !empty(subnetResourceId) ? json('{"subnetResourceId": "${subnetResourceId}"}') : null + apiVersionConstraint: !empty(minApiVersion) ? json('{"minApiVersion": "${minApiVersion}"}') : null + restore: restore + } +} + +module apis_resource 'apis/deploy.bicep' = [for (api, index) in apis: { + name: '${uniqueString(deployment().name, location)}-Apim-Api-${index}' + params: { + apiManagementServiceName: apiManagementService.name + displayName: api.displayName + name: api.name + path: api.path + apiDescription: contains(api, 'apiDescription') ? api.apiDescription : '' + apiRevision: contains(api, 'apiRevision') ? api.apiRevision : '' + apiRevisionDescription: contains(api, 'apiRevisionDescription') ? api.apiRevisionDescription : '' + apiType: contains(api, 'apiType') ? api.apiType : 'http' + apiVersion: contains(api, 'apiVersion') ? api.apiVersion : '' + apiVersionDescription: contains(api, 'apiVersionDescription') ? api.apiVersionDescription : '' + apiVersionSetId: contains(api, 'apiVersionSetId') ? api.apiVersionSetId : '' + authenticationSettings: contains(api, 'authenticationSettings') ? api.authenticationSettings : {} + format: contains(api, 'format') ? api.format : 'openapi' + isCurrent: contains(api, 'isCurrent') ? api.isCurrent : true + protocols: contains(api, 'protocols') ? api.protocols : [ + 'https' + ] + policies: contains(api, 'policies') ? api.policies : [] + serviceUrl: contains(api, 'serviceUrl') ? api.serviceUrl : '' + sourceApiId: contains(api, 'sourceApiId') ? api.sourceApiId : '' + subscriptionKeyParameterNames: contains(api, 'subscriptionKeyParameterNames') ? api.subscriptionKeyParameterNames : {} + subscriptionRequired: contains(api, 'subscriptionRequired') ? api.subscriptionRequired : false + type: contains(api, 'type') ? api.type : 'http' + value: contains(api, 'value') ? api.value : '' + wsdlSelector: contains(api, 'wsdlSelector') ? api.wsdlSelector : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + apiVersionSet_resource + ] +}] + +module apiVersionSet_resource 'apiVersionSets/deploy.bicep' = [for (apiVersionSet, index) in apiVersionSets: { + name: '${uniqueString(deployment().name, location)}-Apim-ApiVersionSet-${index}' + params: { + apiManagementServiceName: apiManagementService.name + name: apiVersionSet.name + properties: contains(apiVersionSet, 'properties') ? apiVersionSet.properties : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module authorizationServers_resource '.bicep/nested_authorizationServers.bicep' = [for (authorizationServer, index) in authorizationServers: { + name: '${uniqueString(deployment().name, location)}-Apim-AuthorizationServer-${index}' + params: { + apiManagementServiceName: apiManagementService.name + name: authorizationServer.name + authorizationEndpoint: authorizationServer.authorizationEndpoint + authorizationMethods: contains(authorizationServer, 'authorizationMethods') ? authorizationServer.authorizationMethods : [ + 'GET' + ] + bearerTokenSendingMethods: contains(authorizationServer, 'bearerTokenSendingMethods') ? authorizationServer.bearerTokenSendingMethods : [ + 'authorizationHeader' + ] + clientAuthenticationMethod: contains(authorizationServer, 'clientAuthenticationMethod') ? authorizationServer.clientAuthenticationMethod : [ + 'Basic' + ] + clientCredentialsKeyVaultId: authorizationServer.clientCredentialsKeyVaultId + clientIdSecretName: authorizationServer.clientIdSecretName + clientSecretSecretName: authorizationServer.clientSecretSecretName + clientRegistrationEndpoint: contains(authorizationServer, 'clientRegistrationEndpoint') ? authorizationServer.clientRegistrationEndpoint : '' + defaultScope: contains(authorizationServer, 'defaultScope') ? authorizationServer.defaultScope : '' + grantTypes: authorizationServer.grantTypes + resourceOwnerPassword: contains(authorizationServer, 'resourceOwnerPassword') ? authorizationServer.resourceOwnerPassword : '' + resourceOwnerUsername: contains(authorizationServer, 'resourceOwnerUsername') ? authorizationServer.resourceOwnerUsername : '' + serverDescription: contains(authorizationServer, 'serverDescription') ? authorizationServer.serverDescription : '' + supportState: contains(authorizationServer, 'supportState') ? authorizationServer.supportState : false + tokenBodyParameters: contains(authorizationServer, 'tokenBodyParameters') ? authorizationServer.tokenBodyParameters : [] + tokenEndpoint: contains(authorizationServer, 'tokenEndpoint') ? authorizationServer.tokenEndpoint : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module backends_resource 'backends/deploy.bicep' = [for (backend, index) in backends: { + name: '${uniqueString(deployment().name, location)}-Apim-Backend-${index}' + params: { + apiManagementServiceName: apiManagementService.name + url: contains(backend, 'url') ? backend.url : '' + backendDescription: contains(backend, 'backendDescription') ? backend.backendDescription : '' + credentials: contains(backend, 'credentials') ? backend.credentials : {} + name: backend.name + protocol: contains(backend, 'protocol') ? backend.protocol : 'http' + proxy: contains(backend, 'proxy') ? backend.proxy : {} + resourceId: contains(backend, 'resourceId') ? backend.resourceId : '' + serviceFabricCluster: contains(backend, 'serviceFabricCluster') ? backend.serviceFabricCluster : {} + title: contains(backend, 'title') ? backend.title : '' + tls: contains(backend, 'tls') ? backend.tls : { + validateCertificateChain: false + validateCertificateName: false + } + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module caches_resource 'caches/deploy.bicep' = [for (cache, index) in caches: { + name: '${uniqueString(deployment().name, location)}-Apim-Cache-${index}' + params: { + apiManagementServiceName: apiManagementService.name + cacheDescription: contains(cache, 'cacheDescription') ? cache.cacheDescription : '' + connectionString: cache.connectionString + name: cache.name + resourceId: contains(cache, 'resourceId') ? cache.resourceId : '' + useFromLocation: cache.useFromLocation + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module identityProvider_resource 'identityProviders/deploy.bicep' = [for (identityProvider, index) in identityProviders: { + name: '${uniqueString(deployment().name, location)}-Apim-IdentityProvider-${index}' + params: { + apiManagementServiceName: apiManagementService.name + name: identityProvider.name + enableIdentityProviders: contains(identityProvider, 'enableIdentityProviders') ? identityProvider.enableIdentityProviders : false + identityProviderAllowedTenants: contains(identityProvider, 'identityProviderAllowedTenants') ? identityProvider.identityProviderAllowedTenants : [] + identityProviderAuthority: contains(identityProvider, 'identityProviderAuthority') ? identityProvider.identityProviderAuthority : '' + identityProviderClientId: contains(identityProvider, 'identityProviderClientId') ? identityProvider.identityProviderClientId : '' + identityProviderClientSecret: contains(identityProvider, 'identityProviderClientSecret') ? identityProvider.identityProviderClientSecret : '' + identityProviderPasswordResetPolicyName: contains(identityProvider, 'identityProviderPasswordResetPolicyName') ? identityProvider.identityProviderPasswordResetPolicyName : '' + identityProviderProfileEditingPolicyName: contains(identityProvider, 'identityProviderProfileEditingPolicyName') ? identityProvider.identityProviderProfileEditingPolicyName : '' + identityProviderSignInPolicyName: contains(identityProvider, 'identityProviderSignInPolicyName') ? identityProvider.identityProviderSignInPolicyName : '' + identityProviderSignInTenant: contains(identityProvider, 'identityProviderSignInTenant') ? identityProvider.identityProviderSignInTenant : '' + identityProviderSignUpPolicyName: contains(identityProvider, 'identityProviderSignUpPolicyName') ? identityProvider.identityProviderSignUpPolicyName : '' + identityProviderType: contains(identityProvider, 'identityProviderType') ? identityProvider.identityProviderType : 'aad' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module namedValues_resource 'namedValues/deploy.bicep' = [for (namedValue, index) in namedValues: { + name: '${uniqueString(deployment().name, location)}-Apim-NamedValue-${index}' + params: { + apiManagementServiceName: apiManagementService.name + displayName: namedValue.displayName + keyVault: contains(namedValue, 'keyVault') ? namedValue.keyVault : {} + name: namedValue.name + namedValueTags: contains(namedValue, 'namedValueTags') ? namedValue.namedValueTags : [] + secret: contains(namedValue, 'secret') ? namedValue.secret : false + value: contains(namedValue, 'value') ? namedValue.value : newGuidValue + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module portalSettings_resource 'portalsettings/deploy.bicep' = [for (portalSetting, index) in portalSettings: { + name: '${uniqueString(deployment().name, location)}-Apim-PortalSetting-${index}' + params: { + apiManagementServiceName: apiManagementService.name + name: portalSetting.name + properties: contains(portalSetting, 'properties') ? portalSetting.properties : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module policy_resource 'policies/deploy.bicep' = [for (policy, index) in policies: { + name: '${uniqueString(deployment().name, location)}-Apim-Policy-${index}' + params: { + apiManagementServiceName: apiManagementService.name + value: policy.value + format: contains(policy, 'format') ? policy.format : 'xml' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module products_resource 'products/deploy.bicep' = [for (product, index) in products: { + name: '${uniqueString(deployment().name, location)}-Apim-Product-${index}' + params: { + apiManagementServiceName: apiManagementService.name + apis: contains(product, 'apis') ? product.apis : [] + approvalRequired: contains(product, 'approvalRequired') ? product.approvalRequired : false + groups: contains(product, 'groups') ? product.groups : [] + name: product.name + productDescription: contains(product, 'productDescription') ? product.productDescription : '' + state: contains(product, 'state') ? product.state : 'published' + subscriptionRequired: contains(product, 'subscriptionRequired') ? product.subscriptionRequired : false + subscriptionsLimit: contains(product, 'subscriptionsLimit') ? product.subscriptionsLimit : 1 + terms: contains(product, 'terms') ? product.terms : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + apis_resource + ] +}] + +module subscriptions_resource 'subscriptions/deploy.bicep' = [for (subscription, index) in subscriptions: { + name: '${uniqueString(deployment().name, location)}-Apim-Subscription-${index}' + params: { + apiManagementServiceName: apiManagementService.name + name: contains(subscription, 'name') ? subscription.name : '' + allowTracing: contains(subscription, 'allowTracing') ? subscription.allowTracing : false + ownerId: contains(subscription, 'ownerId') ? subscription.ownerId : '' + primaryKey: contains(subscription, 'primaryKey') ? subscription.primaryKey : '' + scope: contains(subscription, 'scope') ? subscription.scope : '/apis' + secondaryKey: contains(subscription, 'secondaryKey') ? subscription.secondaryKey : '' + state: contains(subscription, 'state') ? subscription.state : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource apiManagementService_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${apiManagementService.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: apiManagementService +} + +resource apiManagementService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: apiManagementService +} + +module apiManagementService_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Apim-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: apiManagementService.id + } +}] + +@description('The name of the API management service.') +output name string = apiManagementService.name + +@description('The resource ID of the API management service.') +output resourceId string = apiManagementService.id + +@description('The resource group the API management service was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(apiManagementService.identity, 'principalId') ? apiManagementService.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = apiManagementService.location diff --git a/modules/Microsoft.ApiManagement/service/identityProviders/deploy.bicep b/modules/Microsoft.ApiManagement/service/identityProviders/deploy.bicep new file mode 100644 index 0000000..19acb9a --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/identityProviders/deploy.bicep @@ -0,0 +1,95 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Used to enable the deployment of the identityProviders child resource.') +param enableIdentityProviders bool = false + +@description('Optional. List of Allowed Tenants when configuring Azure Active Directory login. - string.') +param identityProviderAllowedTenants array = [] + +@description('Optional. OpenID Connect discovery endpoint hostname for AAD or AAD B2C.') +param identityProviderAuthority string = '' + +@description('Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used.') +param identityProviderClientId string = '' + +@description('Conditional. Client secret of the Application in external Identity Provider, used to authenticate login request. Required if identity provider is used.') +@secure() +param identityProviderClientSecret string = '' + +@description('Optional. Password Reset Policy Name. Only applies to AAD B2C Identity Provider.') +#disable-next-line secure-secrets-in-params // Not a secret +param identityProviderPasswordResetPolicyName string = '' + +@description('Optional. Profile Editing Policy Name. Only applies to AAD B2C Identity Provider.') +param identityProviderProfileEditingPolicyName string = '' + +@description('Optional. Signin Policy Name. Only applies to AAD B2C Identity Provider.') +param identityProviderSignInPolicyName string = '' + +@description('Optional. The TenantId to use instead of Common when logging into Active Directory.') +param identityProviderSignInTenant string = '' + +@description('Optional. Signup Policy Name. Only applies to AAD B2C Identity Provider.') +param identityProviderSignUpPolicyName string = '' + +@description('Optional. Identity Provider Type identifier.') +@allowed([ + 'aad' + 'aadB2C' + 'facebook' + 'google' + 'microsoft' + 'twitter' +]) +param identityProviderType string = 'aad' + +@description('Required. Identity provider name.') +param name string + +var isAadB2C = (identityProviderType == 'aadB2C') + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource identityProvider 'Microsoft.ApiManagement/service/identityProviders@2021-08-01' = if (enableIdentityProviders) { + name: name + parent: service + properties: { + type: identityProviderType + signinTenant: identityProviderSignInTenant + allowedTenants: identityProviderAllowedTenants + authority: identityProviderAuthority + signupPolicyName: isAadB2C ? identityProviderSignUpPolicyName : null + signinPolicyName: isAadB2C ? identityProviderSignInPolicyName : null + profileEditingPolicyName: isAadB2C ? identityProviderProfileEditingPolicyName : null + passwordResetPolicyName: isAadB2C ? identityProviderPasswordResetPolicyName : null + clientId: identityProviderClientId + clientSecret: identityProviderClientSecret + } +} + +@description('The resource ID of the API management service identity provider.') +output resourceId string = identityProvider.id + +@description('The name of the API management service identity provider.') +output name string = identityProvider.name + +@description('The resource group the API management service identity provider was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/identityProviders/readme.md b/modules/Microsoft.ApiManagement/service/identityProviders/readme.md new file mode 100644 index 0000000..b59df8f --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/identityProviders/readme.md @@ -0,0 +1,57 @@ +# API Management Service Identity Providers `[Microsoft.ApiManagement/service/identityProviders]` + +This module deploys API Management Service Identity Provider. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/identityProviders` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/identityProviders) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Identity provider name. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `apiManagementServiceName` | string | | The name of the parent API Management service. Required if the template is used in a standalone deployment. | +| `identityProviderClientId` | string | `''` | Client ID of the Application in the external Identity Provider. Required if identity provider is used. | +| `identityProviderClientSecret` | secureString | `''` | Client secret of the Application in external Identity Provider, used to authenticate login request. Required if identity provider is used. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableIdentityProviders` | bool | `False` | | Used to enable the deployment of the identityProviders child resource. | +| `identityProviderAllowedTenants` | array | `[]` | | List of Allowed Tenants when configuring Azure Active Directory login. - string. | +| `identityProviderAuthority` | string | `''` | | OpenID Connect discovery endpoint hostname for AAD or AAD B2C. | +| `identityProviderPasswordResetPolicyName` | string | `''` | | Password Reset Policy Name. Only applies to AAD B2C Identity Provider. | +| `identityProviderProfileEditingPolicyName` | string | `''` | | Profile Editing Policy Name. Only applies to AAD B2C Identity Provider. | +| `identityProviderSignInPolicyName` | string | `''` | | Signin Policy Name. Only applies to AAD B2C Identity Provider. | +| `identityProviderSignInTenant` | string | `''` | | The TenantId to use instead of Common when logging into Active Directory. | +| `identityProviderSignUpPolicyName` | string | `''` | | Signup Policy Name. Only applies to AAD B2C Identity Provider. | +| `identityProviderType` | string | `'aad'` | `[aad, aadB2C, facebook, google, microsoft, twitter]` | Identity Provider Type identifier. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service identity provider. | +| `resourceGroupName` | string | The resource group the API management service identity provider was deployed into. | +| `resourceId` | string | The resource ID of the API management service identity provider. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/identityProviders/version.json b/modules/Microsoft.ApiManagement/service/identityProviders/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/identityProviders/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/namedValues/deploy.bicep b/modules/Microsoft.ApiManagement/service/namedValues/deploy.bicep new file mode 100644 index 0000000..13260c5 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/namedValues/deploy.bicep @@ -0,0 +1,63 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. Unique name of NamedValue. It may contain only letters, digits, period, dash, and underscore characters.') +param displayName string + +@description('Optional. KeyVault location details of the namedValue.') +param keyVault object = {} + +@description('Required. Named value Name.') +param name string + +@description('Optional. Tags that when provided can be used to filter the NamedValue list. - string.') +param namedValueTags array = [] + +@description('Optional. Determines whether the value is a secret and should be encrypted or not. Default value is false.') +#disable-next-line secure-secrets-in-params // Not a secret +param secret bool = false + +@description('Optional. Value of the NamedValue. Can contain policy expressions. It may not be empty or consist only of whitespace. This property will not be filled on \'GET\' operations! Use \'/listSecrets\' POST request to get the value.') +param value string = newGuid() + +var keyVaultEmpty = empty(keyVault) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource namedValue 'Microsoft.ApiManagement/service/namedValues@2021-08-01' = { + name: name + parent: service + properties: { + tags: !empty(namedValueTags) ? namedValueTags : null + secret: secret + displayName: displayName + value: keyVaultEmpty ? value : null + keyVault: !keyVaultEmpty ? keyVault : null + } +} + +@description('The resource ID of the named value.') +output resourceId string = namedValue.id + +@description('The name of the named value.') +output name string = namedValue.name + +@description('The resource group the named value was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/namedValues/readme.md b/modules/Microsoft.ApiManagement/service/namedValues/readme.md new file mode 100644 index 0000000..378e0c0 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/namedValues/readme.md @@ -0,0 +1,82 @@ +# API Management Service Named Values `[Microsoft.ApiManagement/service/namedValues]` + +This module deploys API Management Service Named Values. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/namedValues` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/namedValues) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `displayName` | string | Unique name of NamedValue. It may contain only letters, digits, period, dash, and underscore characters. | +| `name` | string | Named value Name. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `apiManagementServiceName` | string | `''` | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `keyVault` | object | `{object}` | KeyVault location details of the namedValue. | +| `namedValueTags` | array | `[]` | Tags that when provided can be used to filter the NamedValue list. - string. | +| `secret` | bool | `False` | Determines whether the value is a secret and should be encrypted or not. Default value is false. | +| `value` | string | `[newGuid()]` | Value of the NamedValue. Can contain policy expressions. It may not be empty or consist only of whitespace. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value. | + + +### Parameter Usage: `keyVault` + +

+ +Parameter JSON format + +```json +"keyVault": { + "value":{ + "secretIdentifier":"Key vault secret identifier for fetching secret.", + "identityClientId":"SystemAssignedIdentity or UserAssignedIdentity Client ID which will be used to access key vault secret." + } +} +``` + +
+ +
+ +Bicep format + +```bicep +keyVault: { + secretIdentifier:'Key vault secret identifier for fetching secret.' + identityClientId:'SystemAssignedIdentity or UserAssignedIdentity Client ID which will be used to access key vault secret.' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the named value. | +| `resourceGroupName` | string | The resource group the named value was deployed into. | +| `resourceId` | string | The resource ID of the named value. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/namedValues/version.json b/modules/Microsoft.ApiManagement/service/namedValues/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/namedValues/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/policies/deploy.bicep b/modules/Microsoft.ApiManagement/service/policies/deploy.bicep new file mode 100644 index 0000000..92ffa2e --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/policies/deploy.bicep @@ -0,0 +1,54 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. The name of the policy.') +param name string = 'policy' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Format of the policyContent.') +@allowed([ + 'rawxml' + 'rawxml-link' + 'xml' + 'xml-link' +]) +param format string = 'xml' + +@description('Required. Contents of the Policy as defined by the format.') +param value string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource policy 'Microsoft.ApiManagement/service/policies@2021-08-01' = { + name: name + parent: service + properties: { + format: format + value: value + } +} + +@description('The resource ID of the API management service policy.') +output resourceId string = policy.id + +@description('The name of the API management service policy.') +output name string = policy.name + +@description('The resource group the API management service policy was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/policies/readme.md b/modules/Microsoft.ApiManagement/service/policies/readme.md new file mode 100644 index 0000000..ba3dffd --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/policies/readme.md @@ -0,0 +1,48 @@ +# API Management Service Policies `[Microsoft.ApiManagement/service/policies]` + +This module deploys API Management Service Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/policies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/policies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `value` | string | Contents of the Policy as defined by the format. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `format` | string | `'xml'` | `[rawxml, rawxml-link, xml, xml-link]` | Format of the policyContent. | +| `name` | string | `'policy'` | | The name of the policy. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service policy. | +| `resourceGroupName` | string | The resource group the API management service policy was deployed into. | +| `resourceId` | string | The resource ID of the API management service policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/policies/version.json b/modules/Microsoft.ApiManagement/service/policies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/policies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/portalsettings/deploy.bicep b/modules/Microsoft.ApiManagement/service/portalsettings/deploy.bicep new file mode 100644 index 0000000..2d3b46b --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/portalsettings/deploy.bicep @@ -0,0 +1,47 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. Portal setting name.') +@allowed([ + 'delegation' + 'signin' + 'signup' +]) +param name string + +@description('Optional. Portal setting properties.') +param properties object = {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource portalSetting 'Microsoft.ApiManagement/service/portalsettings@2021-08-01' = if (!empty(properties)) { + name: any(name) + parent: service + properties: properties +} + +@description('The resource ID of the API management service portal setting.') +output resourceId string = portalSetting.id + +@description('The name of the API management service portal setting.') +output name string = portalSetting.name + +@description('The resource group the API management service portal setting was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/portalsettings/readme.md b/modules/Microsoft.ApiManagement/service/portalsettings/readme.md new file mode 100644 index 0000000..4b16d03 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/portalsettings/readme.md @@ -0,0 +1,47 @@ +# API Management Service Portal Settings `[Microsoft.ApiManagement/service/portalsettings]` + +This module deploys API Management Service Portal Setting. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/portalsettings` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/service) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `[delegation, signin, signup]` | Portal setting name. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `properties` | object | `{object}` | Portal setting properties. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service portal setting. | +| `resourceGroupName` | string | The resource group the API management service portal setting was deployed into. | +| `resourceId` | string | The resource ID of the API management service portal setting. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/portalsettings/version.json b/modules/Microsoft.ApiManagement/service/portalsettings/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/portalsettings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/products/apis/deploy.bicep b/modules/Microsoft.ApiManagement/service/products/apis/deploy.bicep new file mode 100644 index 0000000..0180dcd --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/apis/deploy.bicep @@ -0,0 +1,45 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Conditional. The name of the parent Product. Required if the template is used in a standalone deployment.') +param productName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. Name of the product API.') +param name string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName + + resource product 'products@2021-04-01-preview' existing = { + name: productName + } +} + +resource api 'Microsoft.ApiManagement/service/products/apis@2021-08-01' = { + name: name + parent: service::product +} + +@description('The resource ID of the product API.') +output resourceId string = api.id + +@description('The name of the product API.') +output name string = api.name + +@description('The resource group the product API was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/products/apis/readme.md b/modules/Microsoft.ApiManagement/service/products/apis/readme.md new file mode 100644 index 0000000..9e490a4 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/apis/readme.md @@ -0,0 +1,47 @@ +# API Management Service Products APIs `[Microsoft.ApiManagement/service/products/apis]` + +This module deploys API Management Service Product APIs. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/products/apis` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/apis) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the product API. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | +| `productName` | string | The name of the parent Product. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the product API. | +| `resourceGroupName` | string | The resource group the product API was deployed into. | +| `resourceId` | string | The resource ID of the product API. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/products/apis/version.json b/modules/Microsoft.ApiManagement/service/products/apis/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/apis/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/products/deploy.bicep b/modules/Microsoft.ApiManagement/service/products/deploy.bicep new file mode 100644 index 0000000..835db80 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/deploy.bicep @@ -0,0 +1,99 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Whether subscription approval is required. If false, new subscriptions will be approved automatically enabling developers to call the products APIs immediately after subscribing. If true, administrators must manually approve the subscription before the developer can any of the products APIs. Can be present only if subscriptionRequired property is present and has a value of false.') +param approvalRequired bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Product description. May include HTML formatting tags.') +param productDescription string = '' + +@description('Optional. Array of Product APIs.') +param apis array = [] + +@description('Optional. Array of Product Groups.') +param groups array = [] + +@description('Required. Product Name.') +param name string + +@description('Optional. whether product is published or not. Published products are discoverable by users of developer portal. Non published products are visible only to administrators. Default state of Product is notPublished. - notPublished or published.') +param state string = 'published' + +@description('Optional. Whether a product subscription is required for accessing APIs included in this product. If true, the product is referred to as "protected" and a valid subscription key is required for a request to an API included in the product to succeed. If false, the product is referred to as "open" and requests to an API included in the product can be made without a subscription key. If property is omitted when creating a new product it\'s value is assumed to be true.') +param subscriptionRequired bool = false + +@description('Optional. Whether the number of subscriptions a user can have to this product at the same time. Set to null or omit to allow unlimited per user subscriptions. Can be present only if subscriptionRequired property is present and has a value of false.') +param subscriptionsLimit int = 1 + +@description('Optional. Product terms of use. Developers trying to subscribe to the product will be presented and required to accept these terms before they can complete the subscription process.') +param terms string = '' + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource product 'Microsoft.ApiManagement/service/products@2021-08-01' = { + name: name + parent: service + properties: { + description: productDescription + displayName: name + terms: terms + subscriptionRequired: subscriptionRequired + approvalRequired: subscriptionRequired ? approvalRequired : null + subscriptionsLimit: subscriptionRequired ? subscriptionsLimit : null + state: state + } +} + +module product_apis 'apis/deploy.bicep' = [for (api, index) in apis: { + name: '${deployment().name}-Api-${index}' + params: { + apiManagementServiceName: apiManagementServiceName + name: api.name + productName: name + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module product_groups 'groups/deploy.bicep' = [for (group, index) in groups: { + name: '${deployment().name}-Group-${index}' + params: { + apiManagementServiceName: apiManagementServiceName + name: group.name + productName: name + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The resource ID of the API management service product.') +output resourceId string = product.id + +@description('The name of the API management service product.') +output name string = product.name + +@description('The resource group the API management service product was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The Resources IDs of the API management service product APIs.') +output apiResourceIds array = [for index in range(0, length(apis)): product_apis[index].outputs.resourceId] + +@description('The Resources IDs of the API management service product groups.') +output groupResourceIds array = [for index in range(0, length(groups)): product_groups[index].outputs.resourceId] diff --git a/modules/Microsoft.ApiManagement/service/products/groups/deploy.bicep b/modules/Microsoft.ApiManagement/service/products/groups/deploy.bicep new file mode 100644 index 0000000..729680b --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/groups/deploy.bicep @@ -0,0 +1,45 @@ +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Conditional. The name of the parent Product. Required if the template is used in a standalone deployment.') +param productName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. Name of the product group.') +param name string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName + + resource product 'products@2021-04-01-preview' existing = { + name: productName + } +} + +resource group 'Microsoft.ApiManagement/service/products/groups@2021-08-01' = { + name: name + parent: service::product +} + +@description('The resource ID of the product group.') +output resourceId string = group.id + +@description('The name of the product group.') +output name string = group.name + +@description('The resource group the product group was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/products/groups/readme.md b/modules/Microsoft.ApiManagement/service/products/groups/readme.md new file mode 100644 index 0000000..cc65365 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/groups/readme.md @@ -0,0 +1,47 @@ +# API Management Service Products Groups `[Microsoft.ApiManagement/service/products/groups]` + +This module deploys API Management Service Product Groups. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/products/groups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/groups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the product group. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | +| `productName` | string | The name of the parent Product. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the product group. | +| `resourceGroupName` | string | The resource group the product group was deployed into. | +| `resourceId` | string | The resource ID of the product group. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/products/groups/version.json b/modules/Microsoft.ApiManagement/service/products/groups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/groups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/products/readme.md b/modules/Microsoft.ApiManagement/service/products/readme.md new file mode 100644 index 0000000..68150e9 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/readme.md @@ -0,0 +1,58 @@ +# API Management Service Products `[Microsoft.ApiManagement/service/products]` + +This module deploys API Management Service Products. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/products` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products) | +| `Microsoft.ApiManagement/service/products/apis` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/apis) | +| `Microsoft.ApiManagement/service/products/groups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/groups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Product Name. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `apis` | _[apis](apis/readme.md)_ array | `[]` | Array of Product APIs. | +| `approvalRequired` | bool | `False` | Whether subscription approval is required. If false, new subscriptions will be approved automatically enabling developers to call the products APIs immediately after subscribing. If true, administrators must manually approve the subscription before the developer can any of the products APIs. Can be present only if subscriptionRequired property is present and has a value of false. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `groups` | _[groups](groups/readme.md)_ array | `[]` | Array of Product Groups. | +| `productDescription` | string | `''` | Product description. May include HTML formatting tags. | +| `state` | string | `'published'` | whether product is published or not. Published products are discoverable by users of developer portal. Non published products are visible only to administrators. Default state of Product is notPublished. - notPublished or published. | +| `subscriptionRequired` | bool | `False` | Whether a product subscription is required for accessing APIs included in this product. If true, the product is referred to as "protected" and a valid subscription key is required for a request to an API included in the product to succeed. If false, the product is referred to as "open" and requests to an API included in the product can be made without a subscription key. If property is omitted when creating a new product it's value is assumed to be true. | +| `subscriptionsLimit` | int | `1` | Whether the number of subscriptions a user can have to this product at the same time. Set to null or omit to allow unlimited per user subscriptions. Can be present only if subscriptionRequired property is present and has a value of false. | +| `terms` | string | `''` | Product terms of use. Developers trying to subscribe to the product will be presented and required to accept these terms before they can complete the subscription process. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `apiResourceIds` | array | The Resources IDs of the API management service product APIs. | +| `groupResourceIds` | array | The Resources IDs of the API management service product groups. | +| `name` | string | The name of the API management service product. | +| `resourceGroupName` | string | The resource group the API management service product was deployed into. | +| `resourceId` | string | The resource ID of the API management service product. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/products/version.json b/modules/Microsoft.ApiManagement/service/products/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/products/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/readme.md b/modules/Microsoft.ApiManagement/service/readme.md new file mode 100644 index 0000000..eced28f --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/readme.md @@ -0,0 +1,788 @@ +# API Management Services `[Microsoft.ApiManagement/service]` + +This module deploys an API management service. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service) | +| `Microsoft.ApiManagement/service/apis` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apis) | +| `Microsoft.ApiManagement/service/apis/policies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apis/policies) | +| `Microsoft.ApiManagement/service/apiVersionSets` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/apiVersionSets) | +| `Microsoft.ApiManagement/service/authorizationServers` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/authorizationServers) | +| `Microsoft.ApiManagement/service/backends` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/backends) | +| `Microsoft.ApiManagement/service/caches` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/caches) | +| `Microsoft.ApiManagement/service/identityProviders` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/identityProviders) | +| `Microsoft.ApiManagement/service/namedValues` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/namedValues) | +| `Microsoft.ApiManagement/service/policies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/policies) | +| `Microsoft.ApiManagement/service/portalsettings` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/service) | +| `Microsoft.ApiManagement/service/products` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products) | +| `Microsoft.ApiManagement/service/products/apis` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/apis) | +| `Microsoft.ApiManagement/service/products/groups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/products/groups) | +| `Microsoft.ApiManagement/service/subscriptions` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/subscriptions) | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API Management service. | +| `publisherEmail` | string | The email address of the owner of the service. | +| `publisherName` | string | The name of the owner of the service. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalLocations` | array | `[]` | | Additional datacenter locations of the API Management service. | +| `apis` | _[apis](apis/readme.md)_ array | `[]` | | APIs. | +| `apiVersionSets` | _[apiVersionSets](apiVersionSets/readme.md)_ array | `[]` | | API Version Sets. | +| `authorizationServers` | _[authorizationServers](authorizationServers/readme.md)_ array | `[]` | | Authorization servers. | +| `backends` | _[backends](backends/readme.md)_ array | `[]` | | Backends. | +| `caches` | _[caches](caches/readme.md)_ array | `[]` | | Caches. | +| `certificates` | array | `[]` | | List of Certificates that need to be installed in the API Management service. Max supported certificates that can be installed is 10. | +| `customProperties` | object | `{object}` | | Custom properties of the API Management service. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[GatewayLogs]` | `[GatewayLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableGateway` | bool | `False` | | Property only valid for an API Management service deployed in multiple locations. This can be used to disable the gateway in master region. | +| `enableClientCertificate` | bool | `False` | | Property only meant to be used for Consumption SKU Service. This enforces a client certificate to be presented on each request to the gateway. This also enables the ability to authenticate the certificate in the policy on the gateway. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `hostnameConfigurations` | array | `[]` | | Custom hostname configuration of the API Management service. | +| `identityProviders` | _[identityProviders](identityProviders/readme.md)_ array | `[]` | | Identity providers. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `minApiVersion` | string | `''` | | Limit control plane API calls to API Management service with version equal to or newer than this value. | +| `namedValues` | _[namedValues](namedValues/readme.md)_ array | `[]` | | Named values. | +| `newGuidValue` | string | `[newGuid()]` | | Necessary to create a new GUID. | +| `notificationSenderEmail` | string | `'apimgmt-noreply@mail.windowsazure.com'` | | The notification sender email address for the service. | +| `policies` | _[policies](policies/readme.md)_ array | `[]` | | Policies. | +| `portalSettings` | _[portalSettings](portalSettings/readme.md)_ array | `[]` | | Portal settings. | +| `products` | _[products](products/readme.md)_ array | `[]` | | Products. | +| `restore` | bool | `False` | | 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` | array | `[]` | | 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'. | +| `sku` | string | `'Developer'` | `[Basic, Consumption, Developer, Premium, Standard]` | The pricing tier of this API Management service. | +| `skuCount` | int | `1` | `[1, 2]` | The instance size of this API Management service. | +| `subnetResourceId` | string | `''` | | The full resource ID of a subnet in a virtual network to deploy the API Management service in. | +| `subscriptions` | _[subscriptions](subscriptions/readme.md)_ array | `[]` | | Subscriptions. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `virtualNetworkType` | string | `'None'` | `[External, Internal, None]` | The type of VPN in which API Management service needs to be configured in. None (Default Value) means the API Management service is not part of any Virtual Network, External means the API Management deployment is set up inside a Virtual Network having an internet Facing Endpoint, and Internal means that API Management deployment is setup inside a Virtual Network having an Intranet Facing Endpoint only. | +| `zones` | array | `[]` | | A list of availability zones denoting where the resource needs to come from. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `apiManagementServicePolicy` + +

+ +Parameter JSON format + +```json +"apiManagementServicePolicy": { + "value": { + "value":" ", + "format":"xml" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +apiManagementServicePolicy: { + value:' ' + format:'xml' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the API management service. | +| `resourceGroupName` | string | The resource group the API management service was deployed into. | +| `resourceId` | string | The resource ID of the API management service. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Considerations + +- *None* + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.ApiManagement/authorizationServers` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Max

+ +
+ +via Bicep module + +```bicep +module service './Microsoft.ApiManagement/service/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Service' + params: { + // Required parameters + name: '<>-az-apim-max-001' + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '<>-az-amorg-x-001' + // Non-required parameters + apis: [ + { + apiVersionSet: { + name: 'echo-version-set' + properties: { + description: 'echo-version-set' + displayName: 'echo-version-set' + versioningScheme: 'Segment' + } + } + displayName: 'Echo API' + name: 'echo-api' + path: 'echo' + serviceUrl: 'http://echoapi.cloudapp.net/api' + } + ] + authorizationServers: [ + { + authorizationEndpoint: 'https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/authorize' + clientCredentialsKeyVaultId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + clientIdSecretName: 'apimclientid' + clientRegistrationEndpoint: 'http://localhost' + clientSecretSecretName: 'apimclientsecret' + grantTypes: [ + 'authorizationCode' + ] + name: 'AuthServer1' + tokenEndpoint: 'https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token' + } + ] + backends: [ + { + name: 'backend' + tls: { + validateCertificateChain: false + validateCertificateName: false + } + url: 'http://echoapi.cloudapp.net/api' + } + ] + caches: [ + { + connectionString: 'connectionstringtest' + name: 'westeurope' + useFromLocation: 'westeurope' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + identityProviders: [ + { + name: 'aadProvider' + } + ] + lock: 'CanNotDelete' + namedValues: [ + { + displayName: 'apimkey' + name: 'apimkey' + secret: true + } + ] + policies: [ + { + format: 'xml' + value: ' ' + } + ] + portalSettings: [ + { + name: 'signin' + properties: { + enabled: false + } + } + { + name: 'signup' + properties: { + enabled: false + termsOfService: { + consentRequired: false + enabled: false + } + } + } + ] + products: [ + { + apis: [ + { + name: 'echo-api' + } + ] + approvalRequired: false + groups: [ + { + name: 'developers' + } + ] + name: 'Starter' + subscriptionRequired: false + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + subscriptions: [ + { + name: 'testArmSubscriptionAllApis' + scope: '/apis' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-apim-max-001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + }, + // Non-required parameters + "apis": { + "value": [ + { + "apiVersionSet": { + "name": "echo-version-set", + "properties": { + "description": "echo-version-set", + "displayName": "echo-version-set", + "versioningScheme": "Segment" + } + }, + "displayName": "Echo API", + "name": "echo-api", + "path": "echo", + "serviceUrl": "http://echoapi.cloudapp.net/api" + } + ] + }, + "authorizationServers": { + "value": [ + { + "authorizationEndpoint": "https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/authorize", + "clientCredentialsKeyVaultId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "clientIdSecretName": "apimclientid", + "clientRegistrationEndpoint": "http://localhost", + "clientSecretSecretName": "apimclientsecret", + "grantTypes": [ + "authorizationCode" + ], + "name": "AuthServer1", + "tokenEndpoint": "https://login.microsoftonline.com/651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token" + } + ] + }, + "backends": { + "value": [ + { + "name": "backend", + "tls": { + "validateCertificateChain": false, + "validateCertificateName": false + }, + "url": "http://echoapi.cloudapp.net/api" + } + ] + }, + "caches": { + "value": [ + { + "connectionString": "connectionstringtest", + "name": "westeurope", + "useFromLocation": "westeurope" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "identityProviders": { + "value": [ + { + "name": "aadProvider" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "namedValues": { + "value": [ + { + "displayName": "apimkey", + "name": "apimkey", + "secret": true + } + ] + }, + "policies": { + "value": [ + { + "format": "xml", + "value": " " + } + ] + }, + "portalSettings": { + "value": [ + { + "name": "signin", + "properties": { + "enabled": false + } + }, + { + "name": "signup", + "properties": { + "enabled": false, + "termsOfService": { + "consentRequired": false, + "enabled": false + } + } + } + ] + }, + "products": { + "value": [ + { + "apis": [ + { + "name": "echo-api" + } + ], + "approvalRequired": false, + "groups": [ + { + "name": "developers" + } + ], + "name": "Starter", + "subscriptionRequired": false + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "subscriptions": { + "value": [ + { + "name": "testArmSubscriptionAllApis", + "scope": "/apis" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module service './Microsoft.ApiManagement/service/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Service' + params: { + // Required parameters + name: '<>-az-apim-min-001' + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '<>-az-amorg-x-001' + } +} +``` + +
+

+ +

+ +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": "<>-az-apim-min-001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module service './Microsoft.ApiManagement/service/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Service' + params: { + // Required parameters + name: '<>-az-apim-x-001' + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '<>-az-amorg-x-001' + // Non-required parameters + lock: 'CanNotDelete' + policies: [ + { + format: 'xml' + value: ' ' + } + ] + portalSettings: [ + { + name: 'signin' + properties: { + enabled: false + } + } + { + name: 'signup' + properties: { + enabled: false + termsOfService: { + consentRequired: false + enabled: false + } + } + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-apim-x-001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "<>-az-amorg-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "policies": { + "value": [ + { + "format": "xml", + "value": " " + } + ] + }, + "portalSettings": { + "value": [ + { + "name": "signin", + "properties": { + "enabled": false + } + }, + { + "name": "signup", + "properties": { + "enabled": false, + "termsOfService": { + "consentRequired": false, + "enabled": false + } + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ApiManagement/service/subscriptions/deploy.bicep b/modules/Microsoft.ApiManagement/service/subscriptions/deploy.bicep new file mode 100644 index 0000000..44b4dd5 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/subscriptions/deploy.bicep @@ -0,0 +1,65 @@ +@description('Optional. Determines whether tracing can be enabled.') +param allowTracing bool = true + +@description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') +param apiManagementServiceName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. User (user ID path) for whom subscription is being created in form /users/{userId}.') +param ownerId string = '' + +@description('Optional. Primary subscription key. If not specified during request key will be generated automatically.') +param primaryKey string = '' + +@description('Optional. Scope type to choose between a product, "allAPIs" or a specific API. Scope like "/products/{productId}" or "/apis" or "/apis/{apiId}".') +param scope string = '/apis' + +@description('Optional. Secondary subscription key. If not specified during request key will be generated automatically.') +param secondaryKey string = '' + +@description('Optional. Initial subscription state. If no value is specified, subscription is created with Submitted state. Possible states are "*" active "?" the subscription is active, "*" suspended "?" the subscription is blocked, and the subscriber cannot call any APIs of the product, * submitted ? the subscription request has been made by the developer, but has not yet been approved or rejected, * rejected ? the subscription request has been denied by an administrator, * cancelled ? the subscription has been cancelled by the developer or administrator, * expired ? the subscription reached its expiration date and was deactivated. - suspended, active, expired, submitted, rejected, cancelled.') +param state string = '' + +@description('Required. Subscription name.') +param name string + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource service 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apiManagementServiceName +} + +resource subscription 'Microsoft.ApiManagement/service/subscriptions@2021-08-01' = { + name: name + parent: service + properties: { + scope: scope + displayName: name + ownerId: !empty(ownerId) ? ownerId : null + primaryKey: !empty(primaryKey) ? primaryKey : null + secondaryKey: !empty(secondaryKey) ? secondaryKey : null + state: !empty(state) ? state : null + allowTracing: allowTracing + } +} + +@description('The resource ID of the API management service subscription.') +output resourceId string = subscription.id + +@description('The name of the API management service subscription.') +output name string = subscription.name + +@description('The resource group the API management service subscription was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ApiManagement/service/subscriptions/readme.md b/modules/Microsoft.ApiManagement/service/subscriptions/readme.md new file mode 100644 index 0000000..84219b1 --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/subscriptions/readme.md @@ -0,0 +1,52 @@ +# API Management Subscriptions `[Microsoft.ApiManagement/service/subscriptions]` + +This module deploys API Management Subscriptions. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/subscriptions` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2021-08-01/service/subscriptions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Subscription name. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `apiManagementServiceName` | string | The name of the parent API Management service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `allowTracing` | bool | `True` | Determines whether tracing can be enabled. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ownerId` | string | `''` | User (user ID path) for whom subscription is being created in form /users/{userId}. | +| `primaryKey` | string | `''` | Primary subscription key. If not specified during request key will be generated automatically. | +| `scope` | string | `'/apis'` | Scope type to choose between a product, "allAPIs" or a specific API. Scope like "/products/{productId}" or "/apis" or "/apis/{apiId}". | +| `secondaryKey` | string | `''` | Secondary subscription key. If not specified during request key will be generated automatically. | +| `state` | string | `''` | Initial subscription state. If no value is specified, subscription is created with Submitted state. Possible states are "*" active "?" the subscription is active, "*" suspended "?" the subscription is blocked, and the subscriber cannot call any APIs of the product, * submitted ? the subscription request has been made by the developer, but has not yet been approved or rejected, * rejected ? the subscription request has been denied by an administrator, * cancelled ? the subscription has been cancelled by the developer or administrator, * expired ? the subscription reached its expiration date and was deactivated. - suspended, active, expired, submitted, rejected, cancelled. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the API management service subscription. | +| `resourceGroupName` | string | The resource group the API management service subscription was deployed into. | +| `resourceId` | string | The resource ID of the API management service subscription. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ApiManagement/service/subscriptions/version.json b/modules/Microsoft.ApiManagement/service/subscriptions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/subscriptions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ApiManagement/service/version.json b/modules/Microsoft.ApiManagement/service/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ApiManagement/service/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.AppConfiguration/configurationStores/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.AppConfiguration/configurationStores/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..8d6fe27 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'App Configuration Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b') + 'App Configuration Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071') + '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 appConfiguration 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appConfiguration.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appConfiguration +}] diff --git a/modules/Microsoft.AppConfiguration/configurationStores/.test/min.parameters.json b/modules/Microsoft.AppConfiguration/configurationStores/.test/min.parameters.json new file mode 100644 index 0000000..ccc759e --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/.test/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": "<>-az-appc-min-001" + } + } +} diff --git a/modules/Microsoft.AppConfiguration/configurationStores/.test/parameters.json b/modules/Microsoft.AppConfiguration/configurationStores/.test/parameters.json new file mode 100644 index 0000000..0b7490e --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/.test/parameters.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appc-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "keyValues": { + "value": [ + { + "name": "keyName", + "value": "valueName", + "contentType": "contentType", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "createMode": { + "value": "Default" + }, + "disableLocalAuth": { + "value": false + }, + "enablePurgeProtection": { + "value": false + }, + "softDeleteRetentionInDays": { + "value": 1 + } + } +} diff --git a/modules/Microsoft.AppConfiguration/configurationStores/.test/pe.parameters.json b/modules/Microsoft.AppConfiguration/configurationStores/.test/pe.parameters.json new file mode 100644 index 0000000..baab61d --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/.test/pe.parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appc-pe-001" + }, + "createMode": { + "value": "Default" + }, + "disableLocalAuth": { + "value": false + }, + "enablePurgeProtection": { + "value": false + }, + "softDeleteRetentionInDays": { + "value": 1 + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "configurationStores", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azconfig.io" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.AppConfiguration/configurationStores/deploy.bicep b/modules/Microsoft.AppConfiguration/configurationStores/deploy.bicep new file mode 100644 index 0000000..ff7f6c1 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/deploy.bicep @@ -0,0 +1,244 @@ +@description('Required. Name of the Azure App Configuration.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + 'Free' + 'Standard' +]) +@description('Optional. Pricing tier of App Configuration.') +param sku string = 'Standard' + +@allowed([ + 'Default' + 'Recover' +]) +@description('Optional. Indicates whether the configuration store need to be recovered.') +param createMode string = 'Default' + +@description('Optional. Disables all authentication methods other than AAD authentication.') +param disableLocalAuth bool = false + +@description('Optional. Property specifying whether protection against purge is enabled for this configuration store.') +param enablePurgeProtection bool = false + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. The amount of time in days that the configuration store will be retained when it is soft deleted.') +@minValue(1) +@maxValue(7) +param softDeleteRetentionInDays int = 1 + +@description('Optional. All Key / Values to create.') +param keyValues array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'HttpRequest' + 'Audit' +]) +param diagnosticLogCategoriesToEnable array = [ + 'HttpRequest' + 'Audit' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource configurationStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } + identity: identity + properties: { + createMode: createMode + disableLocalAuth: disableLocalAuth + enablePurgeProtection: sku == 'Free' ? false : enablePurgeProtection + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : null + softDeleteRetentionInDays: sku == 'Free' ? 0 : softDeleteRetentionInDays + } +} + +module configurationStore_keyValues 'keyValues/deploy.bicep' = [for (keyValue, index) in keyValues: { + name: '${uniqueString(deployment().name, location)}-AppConfig-KeyValues-${index}' + params: { + appConfigurationName: configurationStore.name + name: keyValue.name + value: keyValue.value + contentType: contains(keyValue, 'contentType') ? keyValue.contentType : '' + tags: contains(keyValue, 'tags') ? keyValue.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource configurationStore_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${configurationStore.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: configurationStore +} + +resource configurationStore_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: configurationStore +} + +module configurationStore_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppConfig-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: configurationStore.id + } +}] + +module configurationStore_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-AppConfig-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(configurationStore.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: configurationStore.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The name of the app configuration.') +output name string = configurationStore.name + +@description('The resource ID of the app configuration.') +output resourceId string = configurationStore.id + +@description('The resource group the app configuration store was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(configurationStore.identity, 'principalId') ? configurationStore.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = configurationStore.location diff --git a/modules/Microsoft.AppConfiguration/configurationStores/keyValues/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..44de910 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,67 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'App Configuration Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b') + 'App Configuration Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071') + '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 appConfiguration 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appConfiguration.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? principalType : null + } + scope: appConfiguration +}] diff --git a/modules/Microsoft.AppConfiguration/configurationStores/keyValues/deploy.bicep b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/deploy.bicep new file mode 100644 index 0000000..55bc707 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/deploy.bicep @@ -0,0 +1,51 @@ +@description('Required. Name of the key.') +param name string + +@description('Required. Name of the value.') +param value string + +@description('Conditional. The name of the parent app configuration store. Required if the template is used in a standalone deployment.') +param appConfigurationName string + +@description('Optional. The content type of the key-values value. Providing a proper content-type can enable transformations of values when they are retrieved by applications.') +param contentType string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') // update all the descriptions +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appConfiguration 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' existing = { + name: appConfigurationName +} + +resource keyValues 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + name: name + parent: appConfiguration + properties: { + contentType: contentType + tags: tags + value: value + } +} +@description('The name of the key values.') +output name string = keyValues.name + +@description('The resource ID of the key values.') +output resourceId string = keyValues.id + +@description('The resource group the batch account was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.AppConfiguration/configurationStores/keyValues/readme.md b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/readme.md new file mode 100644 index 0000000..3a55085 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/readme.md @@ -0,0 +1,182 @@ +# App Configuration `[Microsoft.AppConfiguration/configurationStores/keyValues]` + +This module deploys an App Configuration Store. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AppConfiguration/configurationStores/keyValues` | [2021-10-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.AppConfiguration/2021-10-01-preview/configurationStores/keyValues) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the key. | +| `value` | string | Name of the value. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appConfigurationName` | string | The name of the parent app configuration store. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `contentType` | string | `''` | The content type of the key-values value. Providing a proper content-type can enable transformations of values when they are retrieved by applications. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key values. | +| `resourceGroupName` | string | The resource group the batch account was deployed into. | +| `resourceId` | string | The resource ID of the key values. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.AppConfiguration/configurationStores/keyValues/version.json b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/keyValues/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.AppConfiguration/configurationStores/readme.md b/modules/Microsoft.AppConfiguration/configurationStores/readme.md new file mode 100644 index 0000000..f7934f0 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/readme.md @@ -0,0 +1,550 @@ +# App Configuration `[Microsoft.AppConfiguration/configurationStores]` + +This module deploys an App Configuration Store. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AppConfiguration/configurationStores` | [2021-10-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.AppConfiguration/2021-10-01-preview/configurationStores) | +| `Microsoft.AppConfiguration/configurationStores/keyValues` | [2021-10-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.AppConfiguration/2021-10-01-preview/configurationStores/keyValues) | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure App Configuration. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `createMode` | string | `'Default'` | `[Default, Recover]` | Indicates whether the configuration store need to be recovered. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Audit, HttpRequest]` | `[Audit, HttpRequest]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableLocalAuth` | bool | `False` | | Disables all authentication methods other than AAD authentication. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enablePurgeProtection` | bool | `False` | | Property specifying whether protection against purge is enabled for this configuration store. | +| `keyValues` | _[keyValues](keyValues/readme.md)_ array | `[]` | | All Key / Values to create. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'Standard'` | `[Free, Standard]` | Pricing tier of App Configuration. | +| `softDeleteRetentionInDays` | int | `1` | | The amount of time in days that the configuration store will be retained when it is soft deleted. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the app configuration. | +| `resourceGroupName` | string | The resource group the app configuration store was deployed into. | +| `resourceId` | string | The resource ID of the app configuration. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module configurationStores './Microsoft.AppConfiguration/configurationStores/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ConfigurationStores' + params: { + name: '<>-az-appc-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appc-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module configurationStores './Microsoft.AppConfiguration/configurationStores/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ConfigurationStores' + params: { + // Required parameters + name: '<>-az-appc-x-001' + // Non-required parameters + createMode: 'Default' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disableLocalAuth: false + enablePurgeProtection: false + keyValues: [ + { + contentType: 'contentType' + name: 'keyName' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + value: 'valueName' + } + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + softDeleteRetentionInDays: 1 + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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": "<>-az-appc-x-001" + }, + // Non-required parameters + "createMode": { + "value": "Default" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disableLocalAuth": { + "value": false + }, + "enablePurgeProtection": { + "value": false + }, + "keyValues": { + "value": [ + { + "contentType": "contentType", + "name": "keyName", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "value": "valueName" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "softDeleteRetentionInDays": { + "value": 1 + }, + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module configurationStores './Microsoft.AppConfiguration/configurationStores/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ConfigurationStores' + params: { + // Required parameters + name: '<>-az-appc-pe-001' + // Non-required parameters + createMode: 'Default' + disableLocalAuth: false + enablePurgeProtection: false + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azconfig.io' + ] + } + service: 'configurationStores' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + softDeleteRetentionInDays: 1 + } +} +``` + +
+

+ +

+ +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": "<>-az-appc-pe-001" + }, + // Non-required parameters + "createMode": { + "value": "Default" + }, + "disableLocalAuth": { + "value": false + }, + "enablePurgeProtection": { + "value": false + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azconfig.io" + ] + }, + "service": "configurationStores", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "softDeleteRetentionInDays": { + "value": 1 + } + } +} +``` + +
+

diff --git a/modules/Microsoft.AppConfiguration/configurationStores/version.json b/modules/Microsoft.AppConfiguration/configurationStores/version.json new file mode 100644 index 0000000..08ec8d7 --- /dev/null +++ b/modules/Microsoft.AppConfiguration/configurationStores/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.3" +} diff --git a/modules/Microsoft.Authorization/locks/.test/rg.parameters.json b/modules/Microsoft.Authorization/locks/.test/rg.parameters.json new file mode 100644 index 0000000..dc4870c --- /dev/null +++ b/modules/Microsoft.Authorization/locks/.test/rg.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "level": { + "value": "CanNotDelete" + }, + "resourceGroupName": { + "value": "adp-<>-az-locks-rg-001" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/locks/deploy.bicep b/modules/Microsoft.Authorization/locks/deploy.bicep new file mode 100644 index 0000000..be930fb --- /dev/null +++ b/modules/Microsoft.Authorization/locks/deploy.bicep @@ -0,0 +1,69 @@ +targetScope = 'subscription' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Name of the Resource Group to assign the lock to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided lock to the resource group.') +param resourceGroupName string = '' + +@description('Optional. Subscription ID of the subscription to assign the lock to. If not provided, will use the current scope for deployment. If no resource group name is provided, the module deploys at subscription level, therefore assigns the provided locks to the subscription.') +param subscriptionId string = subscription().id + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module lock_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-Lock-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: '${subscription().displayName}-${level}-lock' + level: level + notes: notes + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module lock_rg 'resourceGroup/deploy.bicep' = if (!empty(subscriptionId) && !empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-Lock-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: '${resourceGroupName}-${level}-lock' + level: level + notes: notes + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the lock.') +output name string = empty(resourceGroupName) ? lock_sub.outputs.name : lock_rg.outputs.name + +@description('The resource ID of the lock.') +output resourceId string = empty(resourceGroupName) ? lock_sub.outputs.resourceId : lock_rg.outputs.resourceId + +@sys.description('The scope this lock applies to.') +output scope string = empty(resourceGroupName) ? lock_sub.outputs.scope : lock_rg.outputs.scope diff --git a/modules/Microsoft.Authorization/locks/readme.md b/modules/Microsoft.Authorization/locks/readme.md new file mode 100644 index 0000000..a6e0ade --- /dev/null +++ b/modules/Microsoft.Authorization/locks/readme.md @@ -0,0 +1,102 @@ +# Authorization Locks `[Microsoft.Authorization/locks]` + +This module deploys Authorization Locks. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location for all resources. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | +| `resourceGroupName` | string | `''` | Name of the Resource Group to assign the lock to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided lock to the resource group. | +| `subscriptionId` | string | `[subscription().id]` | Subscription ID of the subscription to assign the lock to. If not provided, will use the current scope for deployment. If no resource group name is provided, the module deploys at subscription level, therefore assigns the provided locks to the subscription. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Rg

+ +
+ +via Bicep module + +```bicep +module locks './Microsoft.Authorization/locks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Locks' + params: { + // Required parameters + level: 'CanNotDelete' + // Non-required parameters + resourceGroupName: 'adp-<>-az-locks-rg-001' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "level": { + "value": "CanNotDelete" + }, + // Non-required parameters + "resourceGroupName": { + "value": "adp-<>-az-locks-rg-001" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/locks/resourceGroup/deploy.bicep b/modules/Microsoft.Authorization/locks/resourceGroup/deploy.bicep new file mode 100644 index 0000000..f7058dd --- /dev/null +++ b/modules/Microsoft.Authorization/locks/resourceGroup/deploy.bicep @@ -0,0 +1,49 @@ +targetScope = 'resourceGroup' + +@description('Optional. The name of the lock.') +param name string = '${level}-lock' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource lock 'Microsoft.Authorization/locks@2017-04-01' = { + name: name + properties: { + level: level + notes: notes + } +} + +@description('The name of the lock.') +output name string = lock.name + +@description('The resource ID of the lock.') +output resourceId string = lock.id + +@description('The name of the resource group name the lock was applied to.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The scope this lock applies to.') +output scope string = resourceGroup().id diff --git a/modules/Microsoft.Authorization/locks/resourceGroup/readme.md b/modules/Microsoft.Authorization/locks/resourceGroup/readme.md new file mode 100644 index 0000000..6453e76 --- /dev/null +++ b/modules/Microsoft.Authorization/locks/resourceGroup/readme.md @@ -0,0 +1,44 @@ +# Authorization Locks on Resource Group level `[Microsoft.Authorization/locks/resourceGroup]` + +This module deploys Authorization Locks on Resource Group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `[format('{0}-lock', parameters('level'))]` | The name of the lock. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceGroupName` | string | The name of the resource group name the lock was applied to. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/locks/resourceGroup/version.json b/modules/Microsoft.Authorization/locks/resourceGroup/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Authorization/locks/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Authorization/locks/subscription/deploy.bicep b/modules/Microsoft.Authorization/locks/subscription/deploy.bicep new file mode 100644 index 0000000..e260403 --- /dev/null +++ b/modules/Microsoft.Authorization/locks/subscription/deploy.bicep @@ -0,0 +1,49 @@ +targetScope = 'subscription' + +@description('Optional. The name of the lock.') +param name string = '${level}-lock' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource lock 'Microsoft.Authorization/locks@2017-04-01' = { + name: name + properties: { + level: level + notes: notes + } +} + +@description('The name of the lock.') +output name string = lock.name + +@description('The resource ID of the lock.') +output resourceId string = lock.id + +@description('The subscription name the lock was deployed into.') +output subscriptionName string = subscription().displayName + +@sys.description('The scope this lock applies to.') +output scope string = subscription().id diff --git a/modules/Microsoft.Authorization/locks/subscription/readme.md b/modules/Microsoft.Authorization/locks/subscription/readme.md new file mode 100644 index 0000000..e47ab8f --- /dev/null +++ b/modules/Microsoft.Authorization/locks/subscription/readme.md @@ -0,0 +1,44 @@ +# Authorization Locks on Subscription level `[Microsoft.Authorization/locks/subscription]` + +This module deploys Authorization Locks on Subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `[format('{0}-lock', parameters('level'))]` | The name of the lock. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | +| `subscriptionName` | string | The subscription name the lock was deployed into. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/locks/subscription/version.json b/modules/Microsoft.Authorization/locks/subscription/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Authorization/locks/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Authorization/locks/version.json b/modules/Microsoft.Authorization/locks/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Authorization/locks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/mg.min.parameters.json new file mode 100644 index 0000000..7271e1d --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/mg.min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-mg-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/mg.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/mg.parameters.json new file mode 100644 index 0000000..d0c1451 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/mg.parameters.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-polAss" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the management group scope" + }, + "description": { + "value": "[Description] Policy Assignment at the management group scope" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "location": { + "value": "australiaeast" + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg" + ] + }, + "identity": { + "value": "SystemAssigned" + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/rg.min.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/rg.min.parameters.json new file mode 100644 index 0000000..8dd48a8 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/rg.min.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-rg-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "validation-rg" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/rg.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/rg.parameters.json new file mode 100644 index 0000000..a42c54d --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/rg.parameters.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-rg-polAss" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the resource group scope" + }, + "description": { + "value": "[Description] Policy Assignment at the resource group scope" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "location": { + "value": "australiaeast" + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + ] + }, + "identity": { + "value": "UserAssigned" + }, + "userAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "validation-rg" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/sub.min.parameters.json new file mode 100644 index 0000000..ebadf2e --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/sub.min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-sub-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/.test/sub.parameters.json b/modules/Microsoft.Authorization/policyAssignments/.test/sub.parameters.json new file mode 100644 index 0000000..09f7c95 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/.test/sub.parameters.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-polAss" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the subscription scope" + }, + "description": { + "value": "[Description] Policy Assignment at the subscription scope" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "location": { + "value": "australiaeast" + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg" + ] + }, + "identity": { + "value": "UserAssigned" + }, + "userAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyAssignments/deploy.bicep b/modules/Microsoft.Authorization/policyAssignments/deploy.bicep new file mode 100644 index 0000000..bfd3872 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/deploy.bicep @@ -0,0 +1,155 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes.') +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment.') +param subscriptionId string = '' + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment.') +param resourceGroupName string = '' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyAssignment_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + managementGroupId: managementGroupId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyAssignment_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyAssignment_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Assignment Name.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.name : policyAssignment_rg.outputs.name) + +@sys.description('Policy Assignment principal ID.') +output principalId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.principalId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.principalId : policyAssignment_rg.outputs.principalId) + +@sys.description('Policy Assignment resource ID.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.resourceId : policyAssignment_rg.outputs.resourceId) + +@sys.description('The location the resource was deployed into.') +output location string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.location : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.location : policyAssignment_rg.outputs.location) diff --git a/modules/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep new file mode 100644 index 0000000..a0ba11b --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep @@ -0,0 +1,116 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope.') +@maxLength(24) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var identity_var = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + } + identity: identity_var +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(managementGroupId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = identity == 'SystemAssigned' ? policyAssignment.identity.principalId : '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/policyAssignments', policyAssignment.name) + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/modules/Microsoft.Authorization/policyAssignments/managementGroup/readme.md b/modules/Microsoft.Authorization/policyAssignments/managementGroup/readme.md new file mode 100644 index 0000000..1327e5a --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/managementGroup/readme.md @@ -0,0 +1,56 @@ +# Policy Assignment on Management Group level `[Microsoft.Authorization/policyAssignments/managementGroup]` + +With this module you can perform policy assignments on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyAssignments` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `managementGroupId` | string | `[managementGroup().name]` | | The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `parameters` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `roleDefinitionIds` | array | `[]` | | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyAssignments/managementGroup/version.json b/modules/Microsoft.Authorization/policyAssignments/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyAssignments/readme.md b/modules/Microsoft.Authorization/policyAssignments/readme.md new file mode 100644 index 0000000..4c8c9bd --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/readme.md @@ -0,0 +1,711 @@ +# Policy Assignments `[Microsoft.Authorization/policyAssignments]` + +With this module you can perform policy assignments across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyAssignments` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `managementGroupId` | string | `[managementGroup().name]` | | The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `parameters` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `resourceGroupName` | string | `''` | | The Target Scope for the Policy. The name of the resource group for the policy assignment. | +| `roleDefinitionIds` | array | `[]` | | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| `subscriptionId` | string | `''` | | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policyassignment 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policyassignments.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policyassignment 'yourpath/modules/Microsoft.Authorization.policyAssignments/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-min-mg-polAss' + policyDefinitionID: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + } +} +``` + +
+

+ +

+ +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": "<>-min-mg-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-mg-polAss' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26' + // Non-required parameters + description: '[Description] Policy Assignment at the management group scope' + displayName: '[Display Name] Policy Assignment at the management group scope' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: 'australiaeast' + managementGroupId: '<>' + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/resourceGroups/validation-rg' + ] + parameters: { + tagName: { + value: 'env' + } + tagValue: { + value: 'prod' + } + } + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + } +} +``` + +
+

+ +

+ +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": "<>-mg-polAss" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the management group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the management group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "SystemAssigned" + }, + "location": { + "value": "australiaeast" + }, + "managementGroupId": { + "value": "<>" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg" + ] + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + } + } +} +``` + +
+

+ +

Example 3: Rg Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-min-rg-polAss' + policyDefinitionID: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + resourceGroupName: 'validation-rg' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-min-rg-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "resourceGroupName": { + "value": "validation-rg" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Rg

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-rg-polAss' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26' + // Non-required parameters + description: '[Description] Policy Assignment at the resource group scope' + displayName: '[Display Name] Policy Assignment at the resource group scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: 'australiaeast' + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + ] + parameters: { + tagName: { + value: 'env' + } + tagValue: { + value: 'prod' + } + } + resourceGroupName: 'validation-rg' + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '<>' + userAssignedIdentityId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + } +} +``` + +
+

+ +

+ +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": "<>-rg-polAss" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the resource group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the resource group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "australiaeast" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + ] + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "resourceGroupName": { + "value": "validation-rg" + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "<>" + }, + "userAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + } + } +} +``` + +
+

+ +

Example 5: Sub Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-min-sub-polAss' + policyDefinitionID: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-min-sub-polAss" + }, + "policyDefinitionID": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 6: Sub

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyAssignments' + params: { + // Required parameters + name: '<>-sub-polAss' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26' + // Non-required parameters + description: '[Description] Policy Assignment at the subscription scope' + displayName: '[Display Name] Policy Assignment at the subscription scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: 'australiaeast' + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/resourceGroups/validation-rg' + ] + parameters: { + tagName: { + value: 'env' + } + tagValue: { + value: 'prod' + } + } + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '<>' + userAssignedIdentityId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + } +} +``` + +
+

+ +

+ +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": "<>-sub-polAss" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/4f9dc7db-30c1-420c-b61a-e1d640128d26" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the subscription scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the subscription scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "australiaeast" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg" + ] + }, + "parameters": { + "value": { + "tagName": { + "value": "env" + }, + "tagValue": { + "value": "prod" + } + } + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "<>" + }, + "userAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep new file mode 100644 index 0000000..0f5bf6f --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep @@ -0,0 +1,121 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var identity_var = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + } + identity: identity_var +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, resourceGroupName, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = identity == 'SystemAssigned' ? policyAssignment.identity.principalId : '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = az.resourceId(subscriptionId, resourceGroupName, 'Microsoft.Authorization/policyAssignments', policyAssignment.name) + +@sys.description('The name of the resource group the policy was assigned to.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/modules/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md new file mode 100644 index 0000000..ff605aa --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md @@ -0,0 +1,58 @@ +# Policy Assignment on Resource Group level `[Microsoft.Authorization/policyAssignments/resourceGroup]` + +With this module you can perform policy assignments on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyAssignments` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `parameters` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `resourceGroupName` | string | `[resourceGroup().name]` | | The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment. | +| `roleDefinitionIds` | array | `[]` | | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceGroupName` | string | The name of the resource group the policy was assigned to. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyAssignments/resourceGroup/version.json b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep b/modules/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep new file mode 100644 index 0000000..0375ee6 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep @@ -0,0 +1,116 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var identity_var = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + } + identity: identity_var +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = identity == 'SystemAssigned' ? policyAssignment.identity.principalId : '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/policyAssignments', policyAssignment.name) + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/modules/Microsoft.Authorization/policyAssignments/subscription/readme.md b/modules/Microsoft.Authorization/policyAssignments/subscription/readme.md new file mode 100644 index 0000000..bf845e7 --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/subscription/readme.md @@ -0,0 +1,56 @@ +# Policy Assignment on Subscription level `[Microsoft.Authorization/policyAssignments/subscription]` + +With this module you can perform policy assignments on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyAssignments` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `parameters` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `roleDefinitionIds` | array | `[]` | | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyAssignments/subscription/version.json b/modules/Microsoft.Authorization/policyAssignments/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyAssignments/version.json b/modules/Microsoft.Authorization/policyAssignments/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyAssignments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/policyDefinitions/.test/mg.min.parameters.json new file mode 100644 index 0000000..431a0f6 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/.test/mg.min.parameters.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-min-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + }, + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + } + } +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/.test/mg.parameters.json b/modules/Microsoft.Authorization/policyDefinitions/.test/mg.parameters.json new file mode 100644 index 0000000..7196de6 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/.test/mg.parameters.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-policyDef" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at the management group scope" + }, + "description": { + "value": "[Description] This policy definition is deployed at the management group scope" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Resources/subscriptions" + }, + { + "field": "[concat('tags[', parameters('tagName'), ']')]", + "exists": "false" + } + ] + }, + "then": { + "effect": "modify", + "details": { + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ], + "operations": [ + { + "operation": "add", + "field": "[concat('tags[', parameters('tagName'), ']')]", + "value": "[parameters('tagValue')]" + } + ] + } + } + } + }, + "parameters": { + "value": { + "tagName": { + "type": "String", + "metadata": { + "displayName": "Tag Name", + "description": "Name of the tag, such as 'environment'" + } + }, + "tagValue": { + "type": "String", + "metadata": { + "displayName": "Tag Value", + "description": "Value of the tag, such as 'production'" + } + } + } + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/policyDefinitions/.test/sub.min.parameters.json new file mode 100644 index 0000000..f2cd03c --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/.test/sub.min.parameters.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-min-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + }, + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/.test/sub.parameters.json b/modules/Microsoft.Authorization/policyDefinitions/.test/sub.parameters.json new file mode 100644 index 0000000..e445127 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/.test/sub.parameters.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-policyDef" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at subscription scope" + }, + "description": { + "value": "[Description] This policy definition is deployed at subscription scope" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Resources/subscriptions" + }, + { + "field": "[concat('tags[', parameters('tagName'), ']')]", + "exists": "false" + } + ] + }, + "then": { + "effect": "modify", + "details": { + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ], + "operations": [ + { + "operation": "add", + "field": "[concat('tags[', parameters('tagName'), ']')]", + "value": "[parameters('tagValue')]" + } + ] + } + } + } + }, + "parameters": { + "value": { + "tagName": { + "type": "String", + "metadata": { + "displayName": "Tag Name", + "description": "Name of the tag, such as 'environment'" + } + }, + "tagValue": { + "type": "String", + "metadata": { + "displayName": "Tag Value", + "description": "Value of the tag, such as 'production'" + } + } + } + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/deploy.bicep b/modules/Microsoft.Authorization/policyDefinitions/deploy.bicep new file mode 100644 index 0000000..455ad65 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/deploy.bicep @@ -0,0 +1,101 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters for management group scope and subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription (Scope). Cannot be used with managementGroupId.') +param subscriptionId string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + managementGroupId: managementGroupId + mode: mode + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyRule: policyRule + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + subscriptionId: subscriptionId + mode: mode + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyRule: policyRule + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Definition Name.') +output name string = empty(subscriptionId) ? policyDefinition_mg.outputs.name : policyDefinition_sub.outputs.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = empty(subscriptionId) ? policyDefinition_mg.outputs.resourceId : policyDefinition_sub.outputs.resourceId + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = empty(subscriptionId) ? policyDefinition_mg.outputs.roleDefinitionIds : policyDefinition_sub.outputs.roleDefinitionIds diff --git a/modules/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep new file mode 100644 index 0000000..56b8b60 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,75 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. The group ID of the Management Group. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + mode: mode + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyRule: policyRule + } +} + +@sys.description('Policy Definition Name.') +output name string = policyDefinition.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/policyDefinitions', policyDefinition.name) + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = (contains(policyDefinition.properties.policyRule.then, 'details') ? ((contains(policyDefinition.properties.policyRule.then.details, 'roleDefinitionIds') ? policyDefinition.properties.policyRule.then.details.roleDefinitionIds : [])) : []) diff --git a/modules/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md new file mode 100644 index 0000000..545dcea --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md @@ -0,0 +1,49 @@ +# Policy Definitions on Management Group level `[Microsoft.Authorization/policyDefinitions/managementGroup]` + +With this module you can create policy definitions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the Management Group. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyDefinitions/managementGroup/version.json b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/readme.md b/modules/Microsoft.Authorization/policyDefinitions/readme.md new file mode 100644 index 0000000..260c2bc --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/readme.md @@ -0,0 +1,629 @@ +# Policy Definitions `[Microsoft.Authorization/policyDefinitions]` + +With this module you can create policy definitions across the management group or subscription scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters for management group scope and subscription scope. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | +| `subscriptionId` | string | `''` | | The subscription ID of the subscription (Scope). Cannot be used with managementGroupId. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policydefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policydefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policydefinition 'yourpath/modules/Microsoft.Authorization.policyDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyDefinitions' + params: { + // Required parameters + name: '<>-mg-min-policyDef' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters('effect')]' + } + } + // Non-required parameters + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>-mg-min-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + }, + // Non-required parameters + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyDefinitions' + params: { + // Required parameters + name: '<>-mg-policyDef' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat('tags[' parameters('tagName') ']')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat('tags[' parameters('tagName') ']')]' + operation: 'add' + value: '[parameters('tagValue')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + // Non-required parameters + description: '[Description] This policy definition is deployed at the management group scope' + displayName: '[DisplayName] This policy definition is deployed at the management group scope' + managementGroupId: '<>' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as 'environment'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as 'production'' + displayName: 'Tag Value' + } + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>-mg-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.Resources/subscriptions", + "field": "type" + }, + { + "exists": "false", + "field": "[concat('tags[', parameters('tagName'), ']')]" + } + ] + }, + "then": { + "details": { + "operations": [ + { + "field": "[concat('tags[', parameters('tagName'), ']')]", + "operation": "add", + "value": "[parameters('tagValue')]" + } + ], + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ] + }, + "effect": "modify" + } + } + }, + // Non-required parameters + "description": { + "value": "[Description] This policy definition is deployed at the management group scope" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at the management group scope" + }, + "managementGroupId": { + "value": "<>" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "parameters": { + "value": { + "tagName": { + "metadata": { + "description": "Name of the tag, such as 'environment'", + "displayName": "Tag Name" + }, + "type": "String" + }, + "tagValue": { + "metadata": { + "description": "Value of the tag, such as 'production'", + "displayName": "Tag Value" + }, + "type": "String" + } + } + } + } +} +``` + +
+

+ +

Example 3: Sub Min

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyDefinitions' + params: { + // Required parameters + name: '<>-sub-min-policyDef' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters('effect')]' + } + } + // Non-required parameters + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-sub-min-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + }, + // Non-required parameters + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Sub

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyDefinitions' + params: { + // Required parameters + name: '<>-sub-policyDef' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat('tags[' parameters('tagName') ']')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat('tags[' parameters('tagName') ']')]' + operation: 'add' + value: '[parameters('tagValue')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + // Non-required parameters + description: '[Description] This policy definition is deployed at subscription scope' + displayName: '[DisplayName] This policy definition is deployed at subscription scope' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as 'environment'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as 'production'' + displayName: 'Tag Value' + } + type: 'String' + } + } + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-sub-policyDef" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.Resources/subscriptions", + "field": "type" + }, + { + "exists": "false", + "field": "[concat('tags[', parameters('tagName'), ']')]" + } + ] + }, + "then": { + "details": { + "operations": [ + { + "field": "[concat('tags[', parameters('tagName'), ']')]", + "operation": "add", + "value": "[parameters('tagValue')]" + } + ], + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ] + }, + "effect": "modify" + } + } + }, + // Non-required parameters + "description": { + "value": "[Description] This policy definition is deployed at subscription scope" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at subscription scope" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "parameters": { + "value": { + "tagName": { + "metadata": { + "description": "Name of the tag, such as 'environment'", + "displayName": "Tag Name" + }, + "type": "String" + }, + "tagValue": { + "metadata": { + "description": "Value of the tag, such as 'production'", + "displayName": "Tag Value" + }, + "type": "String" + } + } + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep b/modules/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep new file mode 100644 index 0000000..f242761 --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep @@ -0,0 +1,75 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. The subscription ID of the subscription.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + mode: mode + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyRule: policyRule + } +} + +@sys.description('Policy Definition Name.') +output name string = policyDefinition.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/policyDefinitions', policyDefinition.name) + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = (contains(policyDefinition.properties.policyRule.then, 'details') ? ((contains(policyDefinition.properties.policyRule.then.details, 'roleDefinitionIds') ? policyDefinition.properties.policyRule.then.details.roleDefinitionIds : [])) : []) diff --git a/modules/Microsoft.Authorization/policyDefinitions/subscription/readme.md b/modules/Microsoft.Authorization/policyDefinitions/subscription/readme.md new file mode 100644 index 0000000..c8791be --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/subscription/readme.md @@ -0,0 +1,49 @@ +# Policy Definitions on Subscription level `[Microsoft.Authorization/policyDefinitions/subscription]` + +With this module you can create policy definitions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | The subscription ID of the subscription. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyDefinitions/subscription/version.json b/modules/Microsoft.Authorization/policyDefinitions/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyDefinitions/version.json b/modules/Microsoft.Authorization/policyDefinitions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/mg.min.parameters.json new file mode 100644 index 0000000..f5816fc --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/mg.min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-mg-polexem" + }, + "policyAssignmentId": { + "value": "/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/mg.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/mg.parameters.json new file mode 100644 index 0000000..2c76ecb --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/mg.parameters.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-polexem" + }, + "displayName": { + "value": "[Display Name] policy exempt (management group scope)" + }, + "policyAssignmentId": { + "value": "/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "expiresOn": { + "value": "2025-10-02T03:57:00.000Z" + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/rg.min.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/rg.min.parameters.json new file mode 100644 index 0000000..2573b17 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/rg.min.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-rg-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/rg.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/rg.parameters.json new file mode 100644 index 0000000..68fda77 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/rg.parameters.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-rg-polexem" + }, + "displayName": { + "value": "[Display Name] policy exempt (resource group scope)" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "expiresOn": { + "value": "2025-10-02T03:57:00.000Z" + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/sub.min.parameters.json new file mode 100644 index 0000000..920e7d2 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/sub.min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-min-sub-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/.test/sub.parameters.json b/modules/Microsoft.Authorization/policyExemptions/.test/sub.parameters.json new file mode 100644 index 0000000..02b3e90 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/.test/sub.parameters.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-polexem" + }, + "displayName": { + "value": "[Display Name] policy exempt (subscription scope)" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "expiresOn": { + "value": "2025-10-02T03:57:00.000Z" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policyExemptions/deploy.bicep b/modules/Microsoft.Authorization/policyExemptions/deploy.bicep new file mode 100644 index 0000000..a0f74a4 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/deploy.bicep @@ -0,0 +1,124 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for management group, subscription and resource group scopes.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription to be exempted from the policy assignment. Cannot use with management group ID parameter.') +param subscriptionId string = '' + +@sys.description('Optional. The name of the resource group to be exempted from the policy assignment. Must also use the subscription ID parameter.') +param resourceGroupName string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyExemption_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : '' + managementGroupId: managementGroupId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyExemption_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : '' + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyExemption_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : '' + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Exemption Name.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.name : policyExemption_rg.outputs.name) + +@sys.description('Policy Exemption resource ID.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.resourceId : policyExemption_rg.outputs.resourceId) + +@sys.description('Policy Exemption Scope.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.scope : policyExemption_rg.outputs.scope) diff --git a/modules/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep new file mode 100644 index 0000000..1e03185 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep @@ -0,0 +1,75 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for management group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2020-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/policyExemptions', policyExemption.name) + +@sys.description('Policy Exemption Scope.') +output scope string = tenantResourceId('Microsoft.Management/managementGroups', managementGroupId) diff --git a/modules/Microsoft.Authorization/policyExemptions/managementGroup/readme.md b/modules/Microsoft.Authorization/policyExemptions/managementGroup/readme.md new file mode 100644 index 0000000..5cf530c --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/managementGroup/readme.md @@ -0,0 +1,50 @@ +# Policy Exemptions on Management Group level `[Microsoft.Authorization/policyExemptions/managementGroup]` + +With this module you can create policy exemptions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2020-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for management group scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyExemptions/managementGroup/version.json b/modules/Microsoft.Authorization/policyExemptions/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyExemptions/readme.md b/modules/Microsoft.Authorization/policyExemptions/readme.md new file mode 100644 index 0000000..4b73e8d --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/readme.md @@ -0,0 +1,516 @@ +# Policy Exemptions `[Microsoft.Authorization/policyExemptions]` + +With this module you can create policy exemptions across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2020-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for management group, subscription and resource group scopes. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceGroupName` | string | `''` | | The name of the resource group to be exempted from the policy assignment. Must also use the subscription ID parameter. | +| `subscriptionId` | string | `''` | | The subscription ID of the subscription to be exempted from the policy assignment. Cannot use with management group ID parameter. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policyexemption 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policyexemptions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policyexemption 'yourpath/modules/Microsoft.Authorization.policyExemptions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Considerations + +- Policy Exemptions have a dependency on Policy Assignments being applied before creating an exemption. You can use the Policy Assignment [Module](../policyAssignments/deploy.bicep) to deploy a Policy Assignment and then create the exemption for it on the required scope. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-min-mg-polexem' + policyAssignmentId: '/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg' + } +} +``` + +
+

+ +

+ +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": "<>-min-mg-polexem" + }, + "policyAssignmentId": { + "value": "/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg" + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-mg-polexem' + policyAssignmentId: '/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg' + // Non-required parameters + displayName: '[Display Name] policy exempt (management group scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + managementGroupId: '<>' + metadata: { + category: 'Security' + } + } +} +``` + +
+

+ +

+ +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": "<>-mg-polexem" + }, + "policyAssignmentId": { + "value": "/providers/Microsoft.Management/managementGroups/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-mg-pass-loc-rg" + }, + // Non-required parameters + "displayName": { + "value": "[Display Name] policy exempt (management group scope)" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "managementGroupId": { + "value": "<>" + }, + "metadata": { + "value": { + "category": "Security" + } + } + } +} +``` + +
+

+ +

Example 3: Rg Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-min-rg-polexem' + policyAssignmentId: '/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg' + // Non-required parameters + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-min-rg-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + // Non-required parameters + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Rg

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-rg-polexem' + policyAssignmentId: '/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg' + // Non-required parameters + displayName: '[Display Name] policy exempt (resource group scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-rg-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + // Non-required parameters + "displayName": { + "value": "[Display Name] policy exempt (resource group scope)" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 5: Sub Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-min-sub-polexem' + policyAssignmentId: '/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg' + // Non-required parameters + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-min-sub-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + // Non-required parameters + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 6: Sub

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicyExemptions' + params: { + // Required parameters + name: '<>-sub-polexem' + policyAssignmentId: '/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg' + // Non-required parameters + displayName: '[Display Name] policy exempt (subscription scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-sub-polexem" + }, + "policyAssignmentId": { + "value": "/subscriptions/<>/providers/Microsoft.Authorization/policyAssignments/adp-<>-sb-pass-loc-rg" + }, + // Non-required parameters + "displayName": { + "value": "[Display Name] policy exempt (subscription scope)" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep new file mode 100644 index 0000000..6941d76 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep @@ -0,0 +1,77 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for resource group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The subscription ID of the subscription to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The name of the resource group to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2020-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = az.resourceId(subscriptionId, resourceGroupName, 'Microsoft.Authorization/policyExemptions', policyExemption.name) + +@sys.description('Policy Exemption Scope.') +output scope string = resourceGroup().id + +@sys.description('The name of the resource group the policy exemption was applied at.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md new file mode 100644 index 0000000..40c0f74 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md @@ -0,0 +1,51 @@ +# Policy Exemptions on Resource Group level `[Microsoft.Authorization/policyExemptions/resourceGroup]` + +With this module you can create policy exemptions on a resource group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2020-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for resource group scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceGroupName` | string | `[resourceGroup().name]` | | The name of the resource group to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | The subscription ID of the subscription to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceGroupName` | string | The name of the resource group the policy exemption was applied at. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyExemptions/resourceGroup/version.json b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep b/modules/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep new file mode 100644 index 0000000..be4535a --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep @@ -0,0 +1,75 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The subscription ID of the subscription to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2020-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/policyExemptions', policyExemption.name) + +@sys.description('Policy Exemption Scope.') +output scope string = subscription().id diff --git a/modules/Microsoft.Authorization/policyExemptions/subscription/readme.md b/modules/Microsoft.Authorization/policyExemptions/subscription/readme.md new file mode 100644 index 0000000..d9da116 --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/subscription/readme.md @@ -0,0 +1,50 @@ +# Policy Exemptions on Subscription level `[Microsoft.Authorization/policyExemptions/subscription]` + +With this module you can create policy exemptions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2020-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for subscription scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | The subscription ID of the subscription to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policyExemptions/subscription/version.json b/modules/Microsoft.Authorization/policyExemptions/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policyExemptions/version.json b/modules/Microsoft.Authorization/policyExemptions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policyExemptions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.min.parameters.json new file mode 100644 index 0000000..92f9d4a --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.min.parameters.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-min-policySet" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + } + } +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.parameters.json b/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.parameters.json new file mode 100644 index 0000000..029e2d4 --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/.test/mg.parameters.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-mg-policySet" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at management group scope" + }, + "description": { + "value": "[Description] This policy set definition is deployed at management group scope" + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.min.parameters.json new file mode 100644 index 0000000..f6a7e68 --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.min.parameters.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-min-policySet" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.parameters.json b/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.parameters.json new file mode 100644 index 0000000..16a9242 --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/.test/sub.parameters.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-sub-policySet" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at subscription scope" + }, + "description": { + "value": "[Description] This policy set definition is deployed at subscription scope" + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/deploy.bicep b/modules/Microsoft.Authorization/policySetDefinitions/deploy.bicep new file mode 100644 index 0000000..f57db04 --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/deploy.bicep @@ -0,0 +1,91 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative).') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription (Scope). Cannot be used with managementGroupId.') +param subscriptionId string = '' + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policySetDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicySetDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + managementGroupId: managementGroupId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policySetDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicySetDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Set Definition Name.') +output name string = empty(subscriptionId) ? policySetDefinition_mg.outputs.name : policySetDefinition_sub.outputs.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = empty(subscriptionId) ? policySetDefinition_mg.outputs.resourceId : policySetDefinition_sub.outputs.resourceId diff --git a/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep new file mode 100644 index 0000000..54a92f6 --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,65 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative).') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The group ID of the Management Group. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policySetDefinition 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + } +} + +@sys.description('Policy Set Definition Name.') +output name string = policySetDefinition.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/policySetDefinitions', policySetDefinition.name) diff --git a/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md new file mode 100644 index 0000000..7c145bd --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md @@ -0,0 +1,48 @@ +# Policy Set Definitions on Management Group level `[Microsoft.Authorization/policySetDefinitions/managementGroup]` + +With this module you can create policy set definitions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/readme.md b/modules/Microsoft.Authorization/policySetDefinitions/readme.md new file mode 100644 index 0000000..1afe6bb --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/readme.md @@ -0,0 +1,565 @@ +# Policy Set Definitions `[Microsoft.Authorization/policySetDefinitions]` + +With this module you can create policy set definitions across the management group or subscription scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | +| `subscriptionId` | string | `''` | The subscription ID of the subscription (Scope). Cannot be used with managementGroupId. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policysetdefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policysetdefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policysetdefinition 'yourpath/modules/Microsoft.Authorization.policySetDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Considerations + +- Policy Set Definitions (Initiatives) have a dependency on Policy Assignments being applied before creating an initiative. You can use the Policy Assignment [Module](../policyDefinitions/deploy.bicep) to deploy a Policy Definition and then create an initiative for it on the required scope. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicySetDefinitions' + params: { + // Required parameters + name: '<>-mg-min-policySet' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-mg-min-policySet" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicySetDefinitions' + params: { + // Required parameters + name: '<>-mg-policySet' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at management group scope' + displayName: '[DisplayName] This policy set definition is deployed at management group scope' + managementGroupId: '<>' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-mg-policySet" + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + // Non-required parameters + "description": { + "value": "[Description] This policy set definition is deployed at management group scope" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at management group scope" + }, + "managementGroupId": { + "value": "<>" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + } + } +} +``` + +
+

+ +

Example 3: Sub Min

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicySetDefinitions' + params: { + // Required parameters + name: '<>-sub-min-policySet' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + // Non-required parameters + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-sub-min-policySet" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + }, + // Non-required parameters + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Sub

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PolicySetDefinitions' + params: { + // Required parameters + name: '<>-sub-policySet' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at subscription scope' + displayName: '[DisplayName] This policy set definition is deployed at subscription scope' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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": "<>-sub-policySet" + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + // Non-required parameters + "description": { + "value": "[Description] This policy set definition is deployed at subscription scope" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at subscription scope" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep b/modules/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep new file mode 100644 index 0000000..dd74aea --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep @@ -0,0 +1,65 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative). Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The subscription ID of the subscription.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policySetDefinition 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + } +} + +@sys.description('Policy Set Definition Name.') +output name string = policySetDefinition.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/policySetDefinitions', policySetDefinition.name) diff --git a/modules/Microsoft.Authorization/policySetDefinitions/subscription/readme.md b/modules/Microsoft.Authorization/policySetDefinitions/subscription/readme.md new file mode 100644 index 0000000..6bb487b --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/subscription/readme.md @@ -0,0 +1,48 @@ +# Policy Set Definitions on Subscription level `[Microsoft.Authorization/policySetDefinitions/subscription]` + +With this module you can create policy set definitions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). Maximum length is 64 characters for subscription scope. | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | +| `subscriptionId` | string | `[subscription().subscriptionId]` | The subscription ID of the subscription. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/policySetDefinitions/subscription/version.json b/modules/Microsoft.Authorization/policySetDefinitions/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/policySetDefinitions/version.json b/modules/Microsoft.Authorization/policySetDefinitions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/policySetDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/mg.min.parameters.json new file mode 100644 index 0000000..02a4098 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/mg.min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + }, + "principalId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/mg.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/mg.parameters.json new file mode 100644 index 0000000..e6362b6 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/mg.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + "description": { + "value": "Role Assignment (management group scope)" + }, + "principalId": { + "value": "<>" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/rg.min.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/rg.min.parameters.json new file mode 100644 index 0000000..6011dc7 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/rg.min.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + }, + "principalId": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/rg.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/rg.parameters.json new file mode 100644 index 0000000..faf9fc3 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/rg.parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + "description": { + "value": "Role Assignment (resource group scope)" + }, + "principalId": { + "value": "<>" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/sub.min.parameters.json new file mode 100644 index 0000000..2a90f97 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/sub.min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + }, + "principalId": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/.test/sub.parameters.json b/modules/Microsoft.Authorization/roleAssignments/.test/sub.parameters.json new file mode 100644 index 0000000..346ba64 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/.test/sub.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + "description": { + "value": "Role Assignment (subscription scope)" + }, + "principalId": { + "value": "<>" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleAssignments/deploy.bicep b/modules/Microsoft.Authorization/roleAssignments/deploy.bicep new file mode 100644 index 0000000..f2ac30b --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/deploy.bicep @@ -0,0 +1,123 @@ +targetScope = 'managementGroup' + +@sys.description('Required. 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 roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group.') +param resourceGroupName string = '' + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '' + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module roleAssignment_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + managementGroupId: managementGroupId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleAssignment_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleAssignment_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.name : roleAssignment_rg.outputs.name) + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.resourceId : roleAssignment_rg.outputs.resourceId) + +@sys.description('The scope this Role Assignment applies to.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.scope : roleAssignment_rg.outputs.scope) diff --git a/modules/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep new file mode 100644 index 0000000..91b0726 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep @@ -0,0 +1,363 @@ +targetScope = 'managementGroup' + +@sys.description('Required. 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 roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +var builtInRoleNames_var = { + 'AcrPush': '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'AcrPull': '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + 'AcrImageSigner': '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + 'AcrDelete': '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + 'AcrQuarantineReader': '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + 'AcrQuarantineWriter': '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'CosmosBackupOperator': '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + 'Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Owner': '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'Reader': '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'Azure Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Azure Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Azure Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Storage Account Backup Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'CosmosRestoreOperator': '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'Azure Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'CodeSigning Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' +} + +var roleDefinitionId_var = (contains(builtInRoleNames_var, roleDefinitionIdOrName) ? builtInRoleNames_var[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managementGroupId, roleDefinitionId_var, principalId) + properties: { + roleDefinitionId: roleDefinitionId_var + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/roleAssignments', roleAssignment.name) + +@sys.description('The scope this Role Assignment applies to.') +output scope string = tenantResourceId('Microsoft.Management/managementGroups', managementGroupId) diff --git a/modules/Microsoft.Authorization/roleAssignments/managementGroup/readme.md b/modules/Microsoft.Authorization/roleAssignments/managementGroup/readme.md new file mode 100644 index 0000000..fc2bea5 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/managementGroup/readme.md @@ -0,0 +1,49 @@ +# Role Assignment on Management Group level `[Microsoft.Authorization/roleAssignments/managementGroup]` + +With this module you can perform role assignments on a management group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | 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'. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleAssignments/managementGroup/version.json b/modules/Microsoft.Authorization/roleAssignments/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleAssignments/readme.md b/modules/Microsoft.Authorization/roleAssignments/readme.md new file mode 100644 index 0000000..0caeec8 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/readme.md @@ -0,0 +1,498 @@ +# Role Assignments `[Microsoft.Authorization/roleAssignments]` + +This module deploys Role Assignments across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | 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'. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `resourceGroupName` | string | `''` | | Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group. | +| `subscriptionId` | string | `''` | | Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module roleassignment 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.roleassignments.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module roleassignment 'yourpath/modules/Microsoft.Authorization.roleAssignments/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Considerations + +This module can be deployed at the management group, subscription or resource group level + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (management group scope)' + managementGroupId: '<>' + principalType: 'ServicePrincipal' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (management group scope)" + }, + "managementGroupId": { + "value": "<>" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +

Example 3: Rg Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + // Non-required parameters + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + }, + // Non-required parameters + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Rg

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (resource group scope)' + principalType: 'ServicePrincipal' + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (resource group scope)" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 5: Sub Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + // Non-required parameters + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Storage Queue Data Reader" + }, + // Non-required parameters + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 6: Sub

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleAssignments' + params: { + // Required parameters + principalId: '<>' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (subscription scope)' + principalType: 'ServicePrincipal' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "<>" + }, + "roleDefinitionIdOrName": { + "value": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (subscription scope)" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep new file mode 100644 index 0000000..ce78371 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep @@ -0,0 +1,365 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. 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 roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames_var = { + 'AcrPush': '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'AcrPull': '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + 'AcrImageSigner': '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + 'AcrDelete': '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + 'AcrQuarantineReader': '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + 'AcrQuarantineWriter': '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'CosmosBackupOperator': '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + 'Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Owner': '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'Reader': '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'Azure Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Azure Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Azure Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Storage Account Backup Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'CosmosRestoreOperator': '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'Azure Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'CodeSigning Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' +} + +var roleDefinitionId_var = (contains(builtInRoleNames_var, roleDefinitionIdOrName) ? builtInRoleNames_var[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, resourceGroupName, roleDefinitionId_var, principalId) + properties: { + roleDefinitionId: roleDefinitionId_var + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = az.resourceId(resourceGroupName, 'Microsoft.Authorization/roleAssignments', roleAssignment.name) + +@sys.description('The name of the resource group the role assignment was applied at.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The scope this Role Assignment applies to.') +output scope string = resourceGroup().id diff --git a/modules/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md new file mode 100644 index 0000000..83d55ba --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md @@ -0,0 +1,50 @@ +# Role Assignment on Resource Group level `[Microsoft.Authorization/roleAssignments/resourceGroup]` + +With this module you can perform role assignments on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | 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'. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `resourceGroupName` | string | `[resourceGroup().name]` | | Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceGroupName` | string | The name of the resource group the role assignment was applied at. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleAssignments/resourceGroup/version.json b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep b/modules/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep new file mode 100644 index 0000000..ae2b4a4 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep @@ -0,0 +1,363 @@ +targetScope = 'subscription' + +@sys.description('Required. 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 roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames_var = { + 'AcrPush': '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'AcrPull': '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + 'AcrImageSigner': '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + 'AcrDelete': '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + 'AcrQuarantineReader': '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + 'AcrQuarantineWriter': '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'CosmosBackupOperator': '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + 'Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Owner': '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'Reader': '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'Azure Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Azure Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Azure Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Storage Account Backup Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'CosmosRestoreOperator': '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'Azure Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'CodeSigning Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var roleDefinitionId_var = (contains(builtInRoleNames_var, roleDefinitionIdOrName) ? builtInRoleNames_var[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, roleDefinitionId_var, principalId) + properties: { + roleDefinitionId: roleDefinitionId_var + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/roleAssignments', roleAssignment.name) + +@sys.description('The scope this Role Assignment applies to.') +output scope string = subscription().id diff --git a/modules/Microsoft.Authorization/roleAssignments/subscription/readme.md b/modules/Microsoft.Authorization/roleAssignments/subscription/readme.md new file mode 100644 index 0000000..7afec05 --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/subscription/readme.md @@ -0,0 +1,49 @@ +# Role Assignment on Subscription level `[Microsoft.Authorization/roleAssignments/subscription]` + +With this module you can perform role assignments on a subscription level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | 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'. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleAssignments/subscription/version.json b/modules/Microsoft.Authorization/roleAssignments/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleAssignments/version.json b/modules/Microsoft.Authorization/roleAssignments/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleAssignments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/mg.min.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/mg.min.parameters.json new file mode 100644 index 0000000..c4a88ba --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/mg.min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-mg-min" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/read", + "Microsoft.Compute/galleries/images/read" + ] + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/mg.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/mg.parameters.json new file mode 100644 index 0000000..d49ce1c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/mg.parameters.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-mg" + }, + "description": { + "value": "Test Custom Role Definition Standard (management group scope)" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/write", + "Microsoft.Compute/images/delete", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + }, + "assignableScopes": { + "value": [ + "/providers/Microsoft.Management/managementGroups/<>" + ] + }, + "managementGroupId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/rg.min.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/rg.min.parameters.json new file mode 100644 index 0000000..cf6825c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/rg.min.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-rg-min" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/read", + "Microsoft.Compute/galleries/images/read" + ] + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/rg.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/rg.parameters.json new file mode 100644 index 0000000..c27ff2f --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/rg.parameters.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-rg" + }, + "description": { + "value": "Test Custom Role Definition Standard (resource group scope)" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/write", + "Microsoft.Compute/images/delete", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + }, + "assignableScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/<>" + ] + }, + "subscriptionId": { + "value": "<>" + }, + "resourceGroupName": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/sub.min.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/sub.min.parameters.json new file mode 100644 index 0000000..87bbbc2 --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/sub.min.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-sub-min" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/read", + "Microsoft.Compute/galleries/images/read" + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/.test/sub.parameters.json b/modules/Microsoft.Authorization/roleDefinitions/.test/sub.parameters.json new file mode 100644 index 0000000..62e03ca --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/.test/sub.parameters.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "roleName": { + "value": "<>-az-testRole-sub" + }, + "description": { + "value": "Test Custom Role Definition Standard (subscription scope)" + }, + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/write", + "Microsoft.Compute/images/delete", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + }, + "assignableScopes": { + "value": [ + "/subscriptions/<>" + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/deploy.bicep b/modules/Microsoft.Authorization/roleDefinitions/deploy.bicep new file mode 100644 index 0000000..9786701 --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/deploy.bicep @@ -0,0 +1,110 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. Use for both Subscription level and Resource Group Level.') +param subscriptionId string = '' + +@sys.description('Optional. The name of the Resource Group where the Role Definition and Target Scope will be applied to.') +param resourceGroupName string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module roleDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + managementGroupId: managementGroupId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + dataActions: !empty(dataActions) ? dataActions : [] + notDataActions: !empty(notDataActions) ? notDataActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleDefinition_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + dataActions: !empty(dataActions) ? dataActions : [] + notDataActions: !empty(notDataActions) ? notDataActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.name : roleDefinition_rg.outputs.name) + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.resourceId : roleDefinition_rg.outputs.resourceId) + +@sys.description('The scope this Role Definition applies to.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.scope : roleDefinition_rg.outputs.scope) diff --git a/modules/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep new file mode 100644 index 0000000..694c24b --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,63 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' = { + name: guid(roleName, managementGroupId) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + } + ] + assignableScopes: assignableScopes == [] ? array(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId)) : assignableScopes + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = tenantResourceId('Microsoft.Management/managementGroups', managementGroupId) + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId), 'Microsoft.Authorization/roleDefinitions', roleDefinition.name) diff --git a/modules/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md new file mode 100644 index 0000000..abd8afe --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md @@ -0,0 +1,47 @@ +# Role Definitions on Management Group level `[Microsoft.Authorization/roleDefinitions/managementGroup]` + +With this module you can create role definitions on a management group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2018-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2018-01-01-preview/roleDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `notActions` | array | `[]` | List of denied actions. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleDefinitions/managementGroup/version.json b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/readme.md b/modules/Microsoft.Authorization/roleDefinitions/readme.md new file mode 100644 index 0000000..6b55fbd --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/readme.md @@ -0,0 +1,636 @@ +# Role Definitions `[Microsoft.Authorization/roleDefinitions]` + +This module deploys custom RBAC Role Definitions across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2018-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2018-01-01-preview/roleDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `resourceGroupName` | string | `''` | The name of the Resource Group where the Role Definition and Target Scope will be applied to. | +| `subscriptionId` | string | `''` | The subscription ID where the Role Definition and Target Scope will be applied to. Use for both Subscription level and Resource Group Level. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module roledefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.roledefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module roledefinition 'yourpath/modules/Microsoft.Authorization.roleDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Considerations + +This module can be deployed both at subscription or resource group level: + +- To deploy the module at resource group level, provide a valid name of an existing Resource Group in the `resourceGroupName` parameter and an existing subscription ID in the `subscriptionId` parameter. +- To deploy the module at the subscription level, provide an existing subscription ID in the `subscriptionId` parameter. +- To deploy the module at the management group level, provide an existing management group ID in the `managementGroupId` parameter. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-mg-min' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-mg-min" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + } + } +} +``` + +
+

+ +

Example 2: Mg

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-mg' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '/providers/Microsoft.Management/managementGroups/<>' + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (management group scope)' + managementGroupId: '<>' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-mg" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "/providers/Microsoft.Management/managementGroups/<>" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (management group scope)" + }, + "managementGroupId": { + "value": "<>" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + } + } +} +``` + +
+

+ +

Example 3: Rg Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-rg-min' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-rg-min" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + }, + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 4: Rg

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-rg' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '/subscriptions/<>/resourceGroups/<>' + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (resource group scope)' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + resourceGroupName: '<>' + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-rg" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "/subscriptions/<>/resourceGroups/<>" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (resource group scope)" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + }, + "resourceGroupName": { + "value": "<>" + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 5: Sub Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-sub-min' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-sub-min" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

+ +

Example 6: Sub

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RoleDefinitions' + params: { + // Required parameters + roleName: '<>-az-testRole-sub' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '/subscriptions/<>' + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (subscription scope)' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + subscriptionId: '<>' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-az-testRole-sub" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "/subscriptions/<>" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (subscription scope)" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + }, + "subscriptionId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep new file mode 100644 index 0000000..ea92fc6 --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep @@ -0,0 +1,73 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The name of the Resource Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' = { + name: guid(roleName, subscriptionId, resourceGroupName) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + dataActions: dataActions + notDataActions: notDataActions + } + ] + assignableScopes: assignableScopes == [] ? array(resourceGroup().id) : assignableScopes + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = resourceGroup().id + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = roleDefinition.id + +@sys.description('The name of the resource group the role definition was created at.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md new file mode 100644 index 0000000..4d9d4f8 --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md @@ -0,0 +1,50 @@ +# Role Definitions on Resource Group level `[Microsoft.Authorization/roleDefinitions/resourceGroup]` + +With this module you can create role definitions on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2018-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2018-01-01-preview/roleDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `resourceGroupName` | string | `[resourceGroup().name]` | The name of the Resource Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceGroupName` | string | The name of the resource group the role definition was created at. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep b/modules/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep new file mode 100644 index 0000000..d01745b --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep @@ -0,0 +1,71 @@ +targetScope = 'subscription' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' = { + name: guid(roleName, subscriptionId) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + dataActions: dataActions + notDataActions: notDataActions + } + ] + assignableScopes: !empty(assignableScopes) ? assignableScopes : array(subscription().id) + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = subscription().id + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = subscriptionResourceId(subscriptionId, 'Microsoft.Authorization/roleDefinitions', roleDefinition.name) diff --git a/modules/Microsoft.Authorization/roleDefinitions/subscription/readme.md b/modules/Microsoft.Authorization/roleDefinitions/subscription/readme.md new file mode 100644 index 0000000..117ba94 --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/subscription/readme.md @@ -0,0 +1,49 @@ +# Role Definitions on Subscription level `[Microsoft.Authorization/roleDefinitions/subscription]` + +With this module you can create role definitions on a subscription level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2018-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2018-01-01-preview/roleDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Authorization/roleDefinitions/subscription/version.json b/modules/Microsoft.Authorization/roleDefinitions/subscription/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Authorization/roleDefinitions/version.json b/modules/Microsoft.Authorization/roleDefinitions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Authorization/roleDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..c5d21e5 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + '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 automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(automationAccount.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: automationAccount +}] diff --git a/modules/Microsoft.Automation/automationAccounts/.test/encr.parameters.json b/modules/Microsoft.Automation/automationAccounts/.test/encr.parameters.json new file mode 100644 index 0000000..984988d --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/.test/encr.parameters.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-aut-encr-001" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "publicNetworkAccess": { + "value": "Enabled" + } + } +} diff --git a/modules/Microsoft.Automation/automationAccounts/.test/min.parameters.json b/modules/Microsoft.Automation/automationAccounts/.test/min.parameters.json new file mode 100644 index 0000000..c76e891 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/.test/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": "<>-az-aut-min-001" + } + } +} diff --git a/modules/Microsoft.Automation/automationAccounts/.test/parameters.json b/modules/Microsoft.Automation/automationAccounts/.test/parameters.json new file mode 100644 index 0000000..a2a0a55 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/.test/parameters.json @@ -0,0 +1,212 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-aut-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "schedules": { + "value": [ + { + "name": "TestSchedule", + "startTime": "", + "expiryTime": "9999-12-31T13:00", + "interval": 15, + "frequency": "Minute", + "timeZone": "Europe/Berlin", + "advancedSchedule": {} + } + ] + }, + "modules": { + "value": [ + { + "name": "PSWindowsUpdate", + "version": "latest", + "uri": "https://www.powershellgallery.com/api/v2/package" + } + ] + }, + "runbooks": { + "value": [ + { + "name": "TestRunbook", + "runbookType": "PowerShell", + "description": "Test runbook", + "uri": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1", + "version": "1.0.0.0" + } + ] + }, + "jobSchedules": { + "value": [ + { + "scheduleName": "TestSchedule", + "runbookName": "TestRunbook" + } + ] + }, + "variables": { + "value": [ + { + "name": "TestString", + "value": "\"TestString\"", + "description": "TestStringDescription" + }, + { + "name": "TestInteger", + "value": "500", + "description": "TestIntegerDescription" + }, + { + "name": "TestBoolean", + "value": "false", + "description": "TestBooleanDescription" + }, + { + "name": "TestDateTime", + "value": "\"\\/Date(1637934042656)\\/\"", + "description": "TestDateTimeDescription", + "isEncrypted": false + }, + { + "name": "TestEncryptedVariable", + "value": "\"TestEncryptedValue\"", + "description": "TestEncryptedDescription" + } + ] + }, + "linkedWorkspaceResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-aut-001" + }, + "gallerySolutions": { + "value": [ + { + "name": "Updates", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "softwareUpdateConfigurations": { + "value": [ + { + "name": "Windows_ZeroDay", + "frequency": "Month", + "operatingSystem": "Windows", + "rebootSetting": "IfRequired", + "scopeByTags": { + "Update": [ + "Automatic-Wave1" + ] + }, + "maintenanceWindow": "PT4H", + "updateClassifications": [ + "Critical", + "Security", + "UpdateRollup", + "FeaturePack", + "ServicePack", + "Definition", + "Tools", + "Updates" + ], + "includeUpdates": [ + "654321" + ], + "excludeUpdates": [ + "123456" + ], + "interval": 1, + "monthlyOccurrences": [ + { + "occurrence": 3, + "day": "Friday" + } + ], + "startTime": "22:00" + }, + { + "name": "Linux_ZeroDay", + "frequency": "OneTime", + "operatingSystem": "Linux", + "rebootSetting": "IfRequired", + "maintenanceWindow": "PT4H", + "updateClassifications": [ + "Critical", + "Security", + "Other" + ], + "includeUpdates": [ + "kernel" + ], + "excludeUpdates": [ + "icacls" + ], + "startTime": "22:00" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "Webhook", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net" + ] + } + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "DSCAndHybridWorker", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net" + ] + } + } + ] + }, + "disableLocalAuth": { + "value": true + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Automation/automationAccounts/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/deploy.bicep new file mode 100644 index 0000000..8728f6a --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/deploy.bicep @@ -0,0 +1,421 @@ +@description('Required. Name of the Automation Account.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. SKU name of the account.') +@allowed([ + 'Free' + 'Basic' +]) +param skuName string = 'Basic' + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Optional. List of modules to be created in the automation account.') +param modules array = [] + +@description('Optional. List of runbooks to be created in the automation account.') +param runbooks array = [] + +@description('Optional. List of schedules to be created in the automation account.') +param schedules array = [] + +@description('Optional. List of jobSchedules to be created in the automation account.') +param jobSchedules array = [] + +@description('Optional. List of variables to be created in the automation account.') +param variables array = [] + +@description('Optional. ID of the log analytics workspace to be linked to the deployed automation account.') +param linkedWorkspaceResourceId string = '' + +@description('Optional. List of gallerySolutions to be created in the linked log analytics workspace.') +param gallerySolutions array = [] + +@description('Optional. List of softwareUpdateConfigurations to be created in the automation account.') +param softwareUpdateConfigurations array = [] + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. Disable local authentication profile used within the resource.') +param disableLocalAuth bool = true + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@minValue(0) +@maxValue(365) +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the Automation Account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'JobLogs' + 'JobStreams' + 'DscNodeStatus' +]) +param diagnosticLogCategoriesToEnable array = [ + 'JobLogs' + 'JobStreams' + 'DscNodeStatus' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2021-06-22' = { + name: name + location: location + tags: tags + identity: identity + properties: { + sku: { + name: skuName + } + encryption: !empty(cMKKeyName) ? { + keySource: 'Microsoft.KeyVault' + identity: { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } + keyVaultProperties: { + keyName: cMKKeyName + keyVaultUri: cMKKeyVault.properties.vaultUri + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVaultKey.properties.keyUriWithVersion, '/')) + } + } : null + publicNetworkAccess: !empty(publicNetworkAccess) ? (publicNetworkAccess == 'Disabled' ? false : true) : (!empty(privateEndpoints) ? false : null) + disableLocalAuth: disableLocalAuth + } +} + +module automationAccount_modules 'modules/deploy.bicep' = [for (module, index) in modules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Module-${index}' + params: { + name: module.name + automationAccountName: automationAccount.name + version: module.version + uri: module.uri + location: location + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_schedules 'schedules/deploy.bicep' = [for (schedule, index) in schedules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Schedule-${index}' + params: { + name: schedule.name + automationAccountName: automationAccount.name + advancedSchedule: contains(schedule, 'advancedSchedule') ? schedule.advancedSchedule : null + scheduleDescription: contains(schedule, 'description') ? schedule.description : '' + expiryTime: contains(schedule, 'expiryTime') ? schedule.expiryTime : '' + frequency: contains(schedule, 'frequency') ? schedule.frequency : 'OneTime' + interval: contains(schedule, 'interval') ? schedule.interval : 0 + startTime: contains(schedule, 'startTime') ? schedule.startTime : '' + timeZone: contains(schedule, 'timeZone') ? schedule.timeZone : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_runbooks 'runbooks/deploy.bicep' = [for (runbook, index) in runbooks: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Runbook-${index}' + params: { + name: runbook.name + automationAccountName: automationAccount.name + runbookType: runbook.runbookType + runbookDescription: contains(runbook, 'description') ? runbook.description : '' + uri: contains(runbook, 'uri') ? runbook.uri : '' + version: contains(runbook, 'version') ? runbook.version : '' + location: location + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_jobSchedules 'jobSchedules/deploy.bicep' = [for (jobSchedule, index) in jobSchedules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-JobSchedule-${index}' + params: { + automationAccountName: automationAccount.name + runbookName: jobSchedule.runbookName + scheduleName: jobSchedule.scheduleName + parameters: contains(jobSchedule, 'parameters') ? jobSchedule.parameters : {} + runOn: contains(jobSchedule, 'runOn') ? jobSchedule.runOn : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + automationAccount_schedules + automationAccount_runbooks + ] +}] + +module automationAccount_variables 'variables/deploy.bicep' = [for (variable, index) in variables: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Variable-${index}' + params: { + automationAccountName: automationAccount.name + name: variable.name + description: contains(variable, 'description') ? variable.description : '' + value: variable.value + isEncrypted: contains(variable, 'isEncrypted') ? variable.isEncrypted : true + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_linkedService '../../Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep' = if (!empty(linkedWorkspaceResourceId)) { + name: '${uniqueString(deployment().name, location)}-AutoAccount-LinkedService' + params: { + name: 'automation' + logAnalyticsWorkspaceName: last(split(linkedWorkspaceResourceId, '/')) + enableDefaultTelemetry: enableReferencedModulesTelemetry + resourceId: automationAccount.id + tags: tags + } + // This is to support linked services to law in different subscription and resource group than the automation account. + // The current scope is used by default if no linked service is intended to be created. + scope: resourceGroup(!empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[2] : subscription().subscriptionId, !empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[4] : resourceGroup().name) +} + +module automationAccount_solutions '../../Microsoft.OperationsManagement/solutions/deploy.bicep' = [for (gallerySolution, index) in gallerySolutions: if (!empty(linkedWorkspaceResourceId)) { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Solution-${index}' + params: { + name: gallerySolution.name + location: location + logAnalyticsWorkspaceName: last(split(linkedWorkspaceResourceId, '/')) + product: contains(gallerySolution, 'product') ? gallerySolution.product : 'OMSGallery' + publisher: contains(gallerySolution, 'publisher') ? gallerySolution.publisher : 'Microsoft' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + // This is to support solution to law in different subscription and resource group than the automation account. + // The current scope is used by default if no linked service is intended to be created. + scope: resourceGroup(!empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[2] : subscription().subscriptionId, !empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[4] : resourceGroup().name) + dependsOn: [ + automationAccount_linkedService + ] +}] + +module automationAccount_softwareUpdateConfigurations 'softwareUpdateConfigurations/deploy.bicep' = [for (softwareUpdateConfiguration, index) in softwareUpdateConfigurations: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-SwUpdateConfig-${index}' + params: { + name: softwareUpdateConfiguration.name + automationAccountName: automationAccount.name + frequency: softwareUpdateConfiguration.frequency + operatingSystem: softwareUpdateConfiguration.operatingSystem + rebootSetting: softwareUpdateConfiguration.rebootSetting + azureVirtualMachines: contains(softwareUpdateConfiguration, 'azureVirtualMachines') ? softwareUpdateConfiguration.azureVirtualMachines : [] + excludeUpdates: contains(softwareUpdateConfiguration, 'excludeUpdates') ? softwareUpdateConfiguration.excludeUpdates : [] + expiryTime: contains(softwareUpdateConfiguration, 'expiryTime') ? softwareUpdateConfiguration.expiryTime : '' + expiryTimeOffsetMinutes: contains(softwareUpdateConfiguration, 'expiryTimeOffsetMinutes') ? softwareUpdateConfiguration.expiryTimeOffsetMinute : 0 + includeUpdates: contains(softwareUpdateConfiguration, 'includeUpdates') ? softwareUpdateConfiguration.includeUpdates : [] + interval: contains(softwareUpdateConfiguration, 'interval') ? softwareUpdateConfiguration.interval : 1 + isEnabled: contains(softwareUpdateConfiguration, 'isEnabled') ? softwareUpdateConfiguration.isEnabled : true + maintenanceWindow: contains(softwareUpdateConfiguration, 'maintenanceWindow') ? softwareUpdateConfiguration.maintenanceWindow : 'PT2H' + monthDays: contains(softwareUpdateConfiguration, 'monthDays') ? softwareUpdateConfiguration.monthDays : [] + monthlyOccurrences: contains(softwareUpdateConfiguration, 'monthlyOccurrences') ? softwareUpdateConfiguration.monthlyOccurrences : [] + nextRun: contains(softwareUpdateConfiguration, 'nextRun') ? softwareUpdateConfiguration.nextRun : '' + nextRunOffsetMinutes: contains(softwareUpdateConfiguration, 'nextRunOffsetMinutes') ? softwareUpdateConfiguration.nextRunOffsetMinutes : 0 + nonAzureComputerNames: contains(softwareUpdateConfiguration, 'nonAzureComputerNames') ? softwareUpdateConfiguration.nonAzureComputerNames : [] + nonAzureQueries: contains(softwareUpdateConfiguration, 'nonAzureQueries') ? softwareUpdateConfiguration.nonAzureQueries : [] + postTaskParameters: contains(softwareUpdateConfiguration, 'postTaskParameters') ? softwareUpdateConfiguration.postTaskParameters : {} + postTaskSource: contains(softwareUpdateConfiguration, 'postTaskSource') ? softwareUpdateConfiguration.postTaskSource : '' + preTaskParameters: contains(softwareUpdateConfiguration, 'preTaskParameters') ? softwareUpdateConfiguration.preTaskParameters : {} + preTaskSource: contains(softwareUpdateConfiguration, 'preTaskSource') ? softwareUpdateConfiguration.preTaskSource : '' + scheduleDescription: contains(softwareUpdateConfiguration, 'scheduleDescription') ? softwareUpdateConfiguration.scheduleDescription : '' + scopeByLocations: contains(softwareUpdateConfiguration, 'scopeByLocations') ? softwareUpdateConfiguration.scopeByLocations : [] + scopeByResources: contains(softwareUpdateConfiguration, 'scopeByResources') ? softwareUpdateConfiguration.scopeByResources : [ + subscription().id + ] + scopeByTags: contains(softwareUpdateConfiguration, 'scopeByTags') ? softwareUpdateConfiguration.scopeByTags : {} + scopeByTagsOperation: contains(softwareUpdateConfiguration, 'scopeByTagsOperation') ? softwareUpdateConfiguration.scopeByTagsOperation : 'All' + startTime: contains(softwareUpdateConfiguration, 'startTime') ? softwareUpdateConfiguration.startTime : '' + timeZone: contains(softwareUpdateConfiguration, 'timeZone') ? softwareUpdateConfiguration.timeZone : 'UTC' + updateClassifications: contains(softwareUpdateConfiguration, 'updateClassifications') ? softwareUpdateConfiguration.updateClassifications : [ + 'Critical' + 'Security' + ] + weekDays: contains(softwareUpdateConfiguration, 'weekDays') ? softwareUpdateConfiguration.weekDays : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + automationAccount_solutions + ] +}] + +resource automationAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${automationAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: automationAccount +} + +resource automationAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: automationAccount +} + +module automationAccount_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-AutomationAccount-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(automationAccount.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: automationAccount.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module automationAccount_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: automationAccount.id + } +}] + +@description('The name of the deployed automation account.') +output name string = automationAccount.name + +@description('The resource ID of the deployed automation account.') +output resourceId string = automationAccount.id + +@description('The resource group of the deployed automation account.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(automationAccount.identity, 'principalId') ? automationAccount.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = automationAccount.location diff --git a/modules/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep new file mode 100644 index 0000000..9944be2 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep @@ -0,0 +1,62 @@ +@description('Generated. Name of the Automation Account job schedule. Must be a GUID and is autogenerated. No need to provide this value.') +param name string = newGuid() + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. The runbook property associated with the entity.') +param runbookName string + +@description('Required. The schedule property associated with the entity.') +param scheduleName string + +@description('Optional. List of job properties.') +param parameters object = {} + +@description('Optional. The hybrid worker group that the scheduled job should run on.') +param runOn string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: automationAccountName +} + +resource jobSchedule 'Microsoft.Automation/automationAccounts/jobSchedules@2020-01-13-preview' = { + // For each job schedule deployed with an ARM template, the GUID must be unique. Even if you're rescheduling an existing schedule, you'll need to change the GUID. This applies even if you've previously deleted an existing job schedule that was created with the same template. Reusing the same GUID results in a failed deployment. + #disable-next-line use-stable-resource-identifiers + name: name + parent: automationAccount + properties: { + parameters: parameters + runbook: { + name: runbookName + } + runOn: !empty(runOn) ? runOn : null + schedule: { + name: scheduleName + } + } +} + +@description('The name of the deployed job schedule.') +output name string = jobSchedule.name + +@description('The resource ID of the deployed job schedule.') +output resourceId string = jobSchedule.id + +@description('The resource group of the deployed job schedule.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Automation/automationAccounts/jobSchedules/readme.md b/modules/Microsoft.Automation/automationAccounts/jobSchedules/readme.md new file mode 100644 index 0000000..f182483 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/jobSchedules/readme.md @@ -0,0 +1,54 @@ +# Automation Account Job Schedules `[Microsoft.Automation/automationAccounts/jobSchedules]` + +This module deploys an Azure Automation Account Job Schedule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/jobSchedules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/jobSchedules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `runbookName` | string | The runbook property associated with the entity. | +| `scheduleName` | string | The schedule property associated with the entity. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `parameters` | object | `{object}` | List of job properties. | +| `runOn` | string | `''` | The hybrid worker group that the scheduled job should run on. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `[newGuid()]` | Name of the Automation Account job schedule. Must be a GUID and is autogenerated. No need to provide this value. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed job schedule. | +| `resourceGroupName` | string | The resource group of the deployed job schedule. | +| `resourceId` | string | The resource ID of the deployed job schedule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/jobSchedules/version.json b/modules/Microsoft.Automation/automationAccounts/jobSchedules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/jobSchedules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/modules/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/modules/deploy.bicep new file mode 100644 index 0000000..4e9ce28 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/modules/deploy.bicep @@ -0,0 +1,61 @@ +@description('Required. Name of the Automation Account module.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. Module package URI, e.g. https://www.powershellgallery.com/api/v2/package.') +param uri string + +@description('Optional. Module version or specify latest to get the latest version.') +param version string = 'latest' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Automation Account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: automationAccountName +} + +resource module 'Microsoft.Automation/automationAccounts/modules@2020-01-13-preview' = { + name: name + parent: automationAccount + location: location + tags: tags + properties: { + contentLink: { + uri: version != 'latest' ? '${uri}/${name}/${version}' : '${uri}/${name}' + version: version != 'latest' ? version : null + } + } +} + +@description('The name of the deployed module.') +output name string = module.name + +@description('The resource ID of the deployed module.') +output resourceId string = module.id + +@description('The resource group of the deployed module.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = module.location diff --git a/modules/Microsoft.Automation/automationAccounts/modules/readme.md b/modules/Microsoft.Automation/automationAccounts/modules/readme.md new file mode 100644 index 0000000..18989c4 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/modules/readme.md @@ -0,0 +1,92 @@ +# Automation Account Modules `[Microsoft.Automation/automationAccounts/modules]` + +This module deploys an Azure Automation Account Module. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/modules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/modules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account module. | +| `uri` | string | Module package URI, e.g. https://www.powershellgallery.com/api/v2/package. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `tags` | object | `{object}` | Tags of the Automation Account resource. | +| `version` | string | `'latest'` | Module version or specify latest to get the latest version. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed module. | +| `resourceGroupName` | string | The resource group of the deployed module. | +| `resourceId` | string | The resource ID of the deployed module. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/modules/version.json b/modules/Microsoft.Automation/automationAccounts/modules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/modules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/readme.md b/modules/Microsoft.Automation/automationAccounts/readme.md new file mode 100644 index 0000000..2aaee74 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/readme.md @@ -0,0 +1,878 @@ +# Automation Accounts `[Microsoft.Automation/automationAccounts]` + +This module deploys an Azure Automation Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Automation/automationAccounts` | [2021-06-22](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2021-06-22/automationAccounts) | +| `Microsoft.Automation/automationAccounts/jobSchedules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/jobSchedules) | +| `Microsoft.Automation/automationAccounts/modules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/modules) | +| `Microsoft.Automation/automationAccounts/runbooks` | [2019-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/runbooks) | +| `Microsoft.Automation/automationAccounts/schedules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/schedules) | +| `Microsoft.Automation/automationAccounts/softwareUpdateConfigurations` | [2019-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/softwareUpdateConfigurations) | +| `Microsoft.Automation/automationAccounts/variables` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/variables) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `cMKUserAssignedIdentityResourceId` | string | `''` | | User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[DscNodeStatus, JobLogs, JobStreams]` | `[DscNodeStatus, JobLogs, JobStreams]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableLocalAuth` | bool | `True` | | Disable local authentication profile used within the resource. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `gallerySolutions` | array | `[]` | | List of gallerySolutions to be created in the linked log analytics workspace. | +| `jobSchedules` | _[jobSchedules](jobSchedules/readme.md)_ array | `[]` | | List of jobSchedules to be created in the automation account. | +| `linkedWorkspaceResourceId` | string | `''` | | ID of the log analytics workspace to be linked to the deployed automation account. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `modules` | _[modules](modules/readme.md)_ array | `[]` | | List of modules to be created in the automation account. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | 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'. | +| `runbooks` | _[runbooks](runbooks/readme.md)_ array | `[]` | | List of runbooks to be created in the automation account. | +| `schedules` | _[schedules](schedules/readme.md)_ array | `[]` | | List of schedules to be created in the automation account. | +| `skuName` | string | `'Basic'` | `[Basic, Free]` | SKU name of the account. | +| `softwareUpdateConfigurations` | _[softwareUpdateConfigurations](softwareUpdateConfigurations/readme.md)_ array | `[]` | | List of softwareUpdateConfigurations to be created in the automation account. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Automation Account resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `variables` | _[variables](variables/readme.md)_ array | `[]` | | List of variables to be created in the automation account. | + + +### Parameter Usage: `encryption` +Prerequisites: +- User Assigned Identity for Encryption needs `Get`, `List`, `Wrap` and `Unwrap` permissions on the key. +- User Assigned Identity have to be one of the defined identities in userAssignedIdentities parameter block. +- To use Azure Automation with customer managed keys, both `Soft Delete` and `Do Not Purge` features must be turned on to allow for recovery of keys in case of accidental deletion. + +

+ +Parameter JSON format + +```json +"encryptionKeySource" : { + "value" : "Microsoft.KeyVault" +}, +"encryptionUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" // this identity needs to be one of the identities defined in userAssignedIdentities section +}, +"keyName" : { + "value" : "keyEncryptionKey" +}, +"keyvaultUri" : { + "value" : "https://<>.vault.azure.net/" +}, +"keyVersion" : { + "value" : "aa11b22c1234567890c3608c657cd5a2" +}, +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {}, // same value as 'encryptionUserAssignedIdentity' parameter + } +} +``` + +
+ +
+ +Bicep format + +```bicep +encryptionKeySource: 'Microsoft.KeyVault' +encryptionUserAssignedIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' // this identity needs to be one of the identities defined in userAssignedIdentities section +keyName : 'keyEncryptionKey' +keyvaultUri: 'https://<>.vault.azure.net/' +keyVersion: 'aa11b22c1234567890c3608c657cd5a2' +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} // same value as 'encryptionUserAssignedIdentity' parameter +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed automation account. | +| `resourceGroupName` | string | The resource group of the deployed automation account. | +| `resourceId` | string | The resource ID of the deployed automation account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | Local reference | +| `Microsoft.OperationsManagement/solutions` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AutomationAccounts' + params: { + // Required parameters + name: '<>-az-aut-encr-001' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + publicNetworkAccess: 'Enabled' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-aut-encr-001" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AutomationAccounts' + params: { + name: '<>-az-aut-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-aut-min-001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AutomationAccounts' + params: { + // Required parameters + name: '<>-az-aut-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disableLocalAuth: true + gallerySolutions: [ + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + jobSchedules: [ + { + runbookName: 'TestRunbook' + scheduleName: 'TestSchedule' + } + ] + linkedWorkspaceResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-aut-001' + lock: 'CanNotDelete' + modules: [ + { + name: 'PSWindowsUpdate' + uri: 'https://www.powershellgallery.com/api/v2/package' + version: 'latest' + } + ] + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net' + ] + } + service: 'Webhook' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net' + ] + } + service: 'DSCAndHybridWorker' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + runbooks: [ + { + description: 'Test runbook' + name: 'TestRunbook' + runbookType: 'PowerShell' + uri: 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1' + version: '1.0.0.0' + } + ] + schedules: [ + { + advancedSchedule: {} + expiryTime: '9999-12-31T13:00' + frequency: 'Minute' + interval: 15 + name: 'TestSchedule' + startTime: '' + timeZone: 'Europe/Berlin' + } + ] + softwareUpdateConfigurations: [ + { + excludeUpdates: [ + '123456' + ] + frequency: 'Month' + includeUpdates: [ + '654321' + ] + interval: 1 + maintenanceWindow: 'PT4H' + monthlyOccurrences: [ + { + day: 'Friday' + occurrence: 3 + } + ] + name: 'Windows_ZeroDay' + operatingSystem: 'Windows' + rebootSetting: 'IfRequired' + scopeByTags: { + Update: [ + 'Automatic-Wave1' + ] + } + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Definition' + 'FeaturePack' + 'Security' + 'ServicePack' + 'Tools' + 'UpdateRollup' + 'Updates' + ] + } + { + excludeUpdates: [ + 'icacls' + ] + frequency: 'OneTime' + includeUpdates: [ + 'kernel' + ] + maintenanceWindow: 'PT4H' + name: 'Linux_ZeroDay' + operatingSystem: 'Linux' + rebootSetting: 'IfRequired' + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Other' + 'Security' + ] + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + variables: [ + { + description: 'TestStringDescription' + name: 'TestString' + value: '\'TestString\'' + } + { + description: 'TestIntegerDescription' + name: 'TestInteger' + value: '500' + } + { + description: 'TestBooleanDescription' + name: 'TestBoolean' + value: 'false' + } + { + description: 'TestDateTimeDescription' + isEncrypted: false + name: 'TestDateTime' + value: '\'\\/Date(1637934042656)\\/\'' + } + { + description: 'TestEncryptedDescription' + name: 'TestEncryptedVariable' + value: '\'TestEncryptedValue\'' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-aut-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disableLocalAuth": { + "value": true + }, + "gallerySolutions": { + "value": [ + { + "name": "Updates", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "jobSchedules": { + "value": [ + { + "runbookName": "TestRunbook", + "scheduleName": "TestSchedule" + } + ] + }, + "linkedWorkspaceResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-aut-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "modules": { + "value": [ + { + "name": "PSWindowsUpdate", + "uri": "https://www.powershellgallery.com/api/v2/package", + "version": "latest" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net" + ] + }, + "service": "Webhook", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azure-automation.net" + ] + }, + "service": "DSCAndHybridWorker", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "runbooks": { + "value": [ + { + "description": "Test runbook", + "name": "TestRunbook", + "runbookType": "PowerShell", + "uri": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1", + "version": "1.0.0.0" + } + ] + }, + "schedules": { + "value": [ + { + "advancedSchedule": {}, + "expiryTime": "9999-12-31T13:00", + "frequency": "Minute", + "interval": 15, + "name": "TestSchedule", + "startTime": "", + "timeZone": "Europe/Berlin" + } + ] + }, + "softwareUpdateConfigurations": { + "value": [ + { + "excludeUpdates": [ + "123456" + ], + "frequency": "Month", + "includeUpdates": [ + "654321" + ], + "interval": 1, + "maintenanceWindow": "PT4H", + "monthlyOccurrences": [ + { + "day": "Friday", + "occurrence": 3 + } + ], + "name": "Windows_ZeroDay", + "operatingSystem": "Windows", + "rebootSetting": "IfRequired", + "scopeByTags": { + "Update": [ + "Automatic-Wave1" + ] + }, + "startTime": "22:00", + "updateClassifications": [ + "Critical", + "Definition", + "FeaturePack", + "Security", + "ServicePack", + "Tools", + "UpdateRollup", + "Updates" + ] + }, + { + "excludeUpdates": [ + "icacls" + ], + "frequency": "OneTime", + "includeUpdates": [ + "kernel" + ], + "maintenanceWindow": "PT4H", + "name": "Linux_ZeroDay", + "operatingSystem": "Linux", + "rebootSetting": "IfRequired", + "startTime": "22:00", + "updateClassifications": [ + "Critical", + "Other", + "Security" + ] + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "variables": { + "value": [ + { + "description": "TestStringDescription", + "name": "TestString", + "value": "\"TestString\"" + }, + { + "description": "TestIntegerDescription", + "name": "TestInteger", + "value": "500" + }, + { + "description": "TestBooleanDescription", + "name": "TestBoolean", + "value": "false" + }, + { + "description": "TestDateTimeDescription", + "isEncrypted": false, + "name": "TestDateTime", + "value": "\"\\/Date(1637934042656)\\/\"" + }, + { + "description": "TestEncryptedDescription", + "name": "TestEncryptedVariable", + "value": "\"TestEncryptedValue\"" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep new file mode 100644 index 0000000..a51852a --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep @@ -0,0 +1,100 @@ +@description('Required. Name of the Automation Account runbook.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@allowed([ + 'Graph' + 'GraphPowerShell' + 'GraphPowerShellWorkflow' + 'PowerShell' + 'PowerShellWorkflow' +]) +@description('Required. The type of the runbook.') +param runbookType string + +@description('Optional. The description of the runbook.') +param runbookDescription string = '' + +@description('Optional. The uri of the runbook content.') +param uri string = '' + +@description('Optional. The version of the runbook content.') +param version string = '' + +@description('Optional. ID of the runbook storage account.') +param scriptStorageAccountId string = '' + +@description('Generated. Time used as a basis for e.g. the schedule start date.') +param baseTime string = utcNow('u') + +@description('Optional. SAS token validity length. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours.') +param sasTokenValidityLength string = 'PT8H' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Automation Account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var accountSasProperties = { + signedServices: 'b' + signedPermission: 'r' + signedExpiry: dateTimeAdd(baseTime, sasTokenValidityLength) + signedResourceTypes: 'o' + signedProtocol: 'https' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: automationAccountName +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' existing = if (!empty(scriptStorageAccountId)) { + name: last(split(scriptStorageAccountId, '/')) + scope: resourceGroup(split(scriptStorageAccountId, '/')[2], split(scriptStorageAccountId, '/')[4]) +} + +var publishContentLink = empty(uri) ? null : { + uri: !empty(uri) ? (empty(scriptStorageAccountId) ? uri : '${uri}${storageAccount.listAccountSas('2021-04-01', accountSasProperties).accountSasToken}') : null + version: !empty(version) ? version : null +} + +resource runbook 'Microsoft.Automation/automationAccounts/runbooks@2019-06-01' = { + name: name + parent: automationAccount + location: location + tags: tags + properties: { + runbookType: runbookType + description: runbookDescription + publishContentLink: !empty(uri) ? publishContentLink : null + } +} + +@description('The name of the deployed runbook.') +output name string = runbook.name + +@description('The resource ID of the deployed runbook.') +output resourceId string = runbook.id + +@description('The resource group of the deployed runbook.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = runbook.location diff --git a/modules/Microsoft.Automation/automationAccounts/runbooks/readme.md b/modules/Microsoft.Automation/automationAccounts/runbooks/readme.md new file mode 100644 index 0000000..7428c08 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/runbooks/readme.md @@ -0,0 +1,101 @@ +# Automation Account Runbooks `[Microsoft.Automation/automationAccounts/runbooks]` + +This module deploys an Azure Automation Account Runbook. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/runbooks` | [2019-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/runbooks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Name of the Automation Account runbook. | +| `runbookType` | string | `[Graph, GraphPowerShell, GraphPowerShellWorkflow, PowerShell, PowerShellWorkflow]` | The type of the runbook. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `runbookDescription` | string | `''` | The description of the runbook. | +| `sasTokenValidityLength` | string | `'PT8H'` | SAS token validity length. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | +| `scriptStorageAccountId` | string | `''` | ID of the runbook storage account. | +| `tags` | object | `{object}` | Tags of the Automation Account resource. | +| `uri` | string | `''` | The uri of the runbook content. | +| `version` | string | `''` | The version of the runbook content. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Time used as a basis for e.g. the schedule start date. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed runbook. | +| `resourceGroupName` | string | The resource group of the deployed runbook. | +| `resourceId` | string | The resource ID of the deployed runbook. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/runbooks/version.json b/modules/Microsoft.Automation/automationAccounts/runbooks/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/runbooks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/schedules/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/schedules/deploy.bicep new file mode 100644 index 0000000..4c4900d --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/schedules/deploy.bicep @@ -0,0 +1,84 @@ +@description('Required. Name of the Automation Account schedule.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Optional. The properties of the create Advanced Schedule.') +@metadata({ + monthDays: 'Days of the month that the job should execute on. Must be between 1 and 31.' + monthlyOccurrences: 'Occurrences of days within a month.' + weekDays: 'Days of the week that the job should execute on.' +}) +param advancedSchedule object = {} + +@description('Optional. The description of the schedule.') +param scheduleDescription string = '' + +@description('Optional. The end time of the schedule.') +param expiryTime string = '' + +@allowed([ + 'Day' + 'Hour' + 'Minute' + 'Month' + 'OneTime' + 'Week' +]) +@description('Optional. The frequency of the schedule.') +param frequency string = 'OneTime' + +@description('Optional. Anything.') +param interval int = 0 + +@description('Optional. The start time of the schedule.') +param startTime string = '' + +@description('Optional. The time zone of the schedule.') +param timeZone string = '' + +@description('Generated. Time used as a basis for e.g. the schedule start date.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: automationAccountName +} + +resource schedule 'Microsoft.Automation/automationAccounts/schedules@2020-01-13-preview' = { + name: name + parent: automationAccount + properties: { + advancedSchedule: !empty(advancedSchedule) ? advancedSchedule : null + description: !empty(scheduleDescription) ? scheduleDescription : null + expiryTime: !empty(expiryTime) ? expiryTime : null + frequency: !empty(frequency) ? frequency : 'OneTime' + interval: (interval != 0) ? interval : null + startTime: !empty(startTime) ? startTime : dateTimeAdd(baseTime, 'PT10M') + timeZone: !empty(timeZone) ? timeZone : null + } +} + +@description('The name of the deployed schedule.') +output name string = schedule.name + +@description('The resource ID of the deployed schedule.') +output resourceId string = schedule.id + +@description('The resource group of the deployed schedule.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Automation/automationAccounts/schedules/readme.md b/modules/Microsoft.Automation/automationAccounts/schedules/readme.md new file mode 100644 index 0000000..afdcc9a --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/schedules/readme.md @@ -0,0 +1,58 @@ +# Automation Account Schedules `[Microsoft.Automation/automationAccounts/schedules]` + +This module deploys an Azure Automation Account Schedule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/schedules` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/schedules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account schedule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `advancedSchedule` | object | `{object}` | | The properties of the create Advanced Schedule. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `expiryTime` | string | `''` | | The end time of the schedule. | +| `frequency` | string | `'OneTime'` | `[Day, Hour, Minute, Month, OneTime, Week]` | The frequency of the schedule. | +| `interval` | int | `0` | | Anything. | +| `scheduleDescription` | string | `''` | | The description of the schedule. | +| `startTime` | string | `''` | | The start time of the schedule. | +| `timeZone` | string | `''` | | The time zone of the schedule. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Time used as a basis for e.g. the schedule start date. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed schedule. | +| `resourceGroupName` | string | The resource group of the deployed schedule. | +| `resourceId` | string | The resource ID of the deployed schedule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/schedules/version.json b/modules/Microsoft.Automation/automationAccounts/schedules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/schedules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep new file mode 100644 index 0000000..516817d --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep @@ -0,0 +1,273 @@ +@description('Required. The name of the Deployment schedule.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. The operating system to be configured by the deployment schedule.') +@allowed([ + 'Windows' + 'Linux' +]) +param operatingSystem string + +@description('Required. Reboot setting for the deployment schedule.') +@allowed([ + 'IfRequired' + 'Never' + 'RebootOnly' + 'Always' +]) +param rebootSetting string + +@description('Required. The frequency of the deployment schedule. When using \'Hour\', \'Day\', \'Week\' or \'Month\', an interval needs to be provided.') +@allowed([ + 'OneTime' + 'Hour' + 'Day' + 'Week' + 'Month' +]) +param frequency string + +@description('Optional. Maximum time allowed for the deployment schedule to run. Duration needs to be specified using the format PT[n]H[n]M[n]S as per ISO8601.') +param maintenanceWindow string = 'PT2H' + +@description('Optional. Update classification included in the deployment schedule.') +@allowed([ + 'Critical' + 'Security' + 'UpdateRollup' + 'FeaturePack' + 'ServicePack' + 'Definition' + 'Tools' + 'Updates' + 'Other' +]) +param updateClassifications array = [ + 'Critical' + 'Security' +] + +@description('Optional. KB numbers or Linux packages excluded in the deployment schedule.') +param excludeUpdates array = [] + +@description('Optional. KB numbers or Linux packages included in the deployment schedule.') +param includeUpdates array = [] + +@description('Optional. Specify the resources to scope the deployment schedule to.') +param scopeByResources array = [ + subscription().id +] + +@description('Optional. Specify tags to which to scope the deployment schedule to.') +param scopeByTags object = {} + +@description('Optional. Enables the scopeByTags to require All (Tag A and Tag B) or Any (Tag A or Tag B).') +@allowed([ + 'All' + 'Any' +]) +param scopeByTagsOperation string = 'All' + +@description('Optional. Specify locations to which to scope the deployment schedule to.') +param scopeByLocations array = [] + +@description('Optional. Parameters provided to the task running before the deployment schedule.') +param preTaskParameters object = {} + +@description('Optional. The source of the task running before the deployment schedule.') +param preTaskSource string = '' + +@description('Optional. Parameters provided to the task running after the deployment schedule.') +param postTaskParameters object = {} + +@description('Optional. The source of the task running after the deployment schedule.') +param postTaskSource string = '' + +@description('Optional. The interval of the frequency for the deployment schedule. 1 Hour is every hour, 2 Day is every second day, etc.') +@maxValue(100) +param interval int = 1 + +@description('Optional. Enables the deployment schedule.') +param isEnabled bool = true + +@description('Optional. Time zone for the deployment schedule. IANA ID or a Windows Time Zone ID.') +param timeZone string = 'UTC' + +@description('Optional. Array of functions from a Log Analytics workspace, used to scope the deployment schedule.') +param nonAzureQueries array = [] + +@description('Optional. List of azure resource IDs for azure virtual machines in scope for the deployment schedule.') +param azureVirtualMachines array = [] + +@description('Optional. List of names of non-azure machines in scope for the deployment schedule.') +param nonAzureComputerNames array = [] + +@description('Optional. Required when used with frequency \'Week\'. Specified the day of the week to run the deployment schedule.') +@allowed([ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + 'Saturday' + 'Sunday' +]) +param weekDays array = [] + +@description('Optional. Can be used with frequency \'Month\'. Provides the specific days of the month to run the deployment schedule.') +@allowed([ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 +]) +param monthDays array = [] + +@description('Optional. Can be used with frequency \'Month\'. Provides the pattern/cadence for running the deployment schedule in a month. Takes objects formed like this {occurance(int),day(string)}. Day is the name of the day to run the deployment schedule, the occurance specifies which occurance of that day to run the deployment schedule.') +param monthlyOccurrences array = [] + +@description('Optional. The start time of the deployment schedule in ISO 8601 format. To specify a specific time use YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. For schedules where we want to start the deployment as soon as possible, specify the time segment only in 24 hour format, HH:MM, 22:00.') +param startTime string = '' + +@description('Optional. The end time of the deployment schedule in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00.') +param expiryTime string = '' + +@description('Optional. The expiry time\'s offset in minutes.') +param expiryTimeOffsetMinutes int = 0 + +@description('Optional. The next time the deployment schedule runs in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00.') +param nextRun string = '' + +@description('Optional. The next run\'s offset in minutes.') +param nextRunOffsetMinutes int = 0 + +@description('Optional. The schedules description.') +param scheduleDescription string = '' + +@description('Generated. Do not touch. Is used to provide the base time for time comparison for startTime. If startTime is specified in HH:MM format, baseTime is used to check if the provided startTime has passed, adding one day before setting the deployment schedule.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var updateClassifications_var = replace(replace(replace(replace(string(updateClassifications), ',', ', '), '[', ''), ']', ''), '"', '') + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: automationAccountName +} + +resource softwareUpdateConfiguration 'Microsoft.Automation/automationAccounts/softwareUpdateConfigurations@2019-06-01' = { + name: name + parent: automationAccount + properties: { + updateConfiguration: { + operatingSystem: operatingSystem + duration: maintenanceWindow + linux: ((operatingSystem == 'Linux') ? { + excludedPackageNameMasks: excludeUpdates + includedPackageNameMasks: includeUpdates + includedPackageClassifications: updateClassifications_var + rebootSetting: rebootSetting + } : null) + windows: ((operatingSystem == 'Windows') ? { + excludedKbNumbers: excludeUpdates + includedKbNumbers: includeUpdates + includedUpdateClassifications: updateClassifications_var + rebootSetting: rebootSetting + } : null) + targets: { + azureQueries: [ + { + scope: scopeByResources + tagSettings: { + tags: scopeByTags + filterOperator: scopeByTagsOperation + } + locations: scopeByLocations + } + ] + nonAzureQueries: nonAzureQueries + } + azureVirtualMachines: azureVirtualMachines + nonAzureComputerNames: nonAzureComputerNames + } + tasks: { + preTask: { + parameters: (empty(preTaskParameters) ? null : preTaskParameters) + source: (empty(preTaskSource) ? null : preTaskSource) + } + postTask: { + parameters: (empty(postTaskParameters) ? null : postTaskParameters) + source: (empty(postTaskSource) ? null : postTaskSource) + } + } + scheduleInfo: { + interval: interval + frequency: frequency + isEnabled: isEnabled + timeZone: timeZone + advancedSchedule: { + weekDays: (empty(weekDays) ? null : weekDays) + monthDays: (empty(monthDays) ? null : monthDays) + monthlyOccurrences: (empty(monthlyOccurrences) ? null : monthlyOccurrences) + } + startTime: (empty(startTime) ? dateTimeAdd(baseTime, 'PT10M') : startTime) + expiryTime: expiryTime + expiryTimeOffsetMinutes: expiryTimeOffsetMinutes + nextRun: nextRun + nextRunOffsetMinutes: nextRunOffsetMinutes + description: scheduleDescription + } + } +} + +@description('The name of the deployed softwareUpdateConfiguration.') +output name string = softwareUpdateConfiguration.name + +@description('The resource ID of the deployed softwareUpdateConfiguration.') +output resourceId string = softwareUpdateConfiguration.id + +@description('The resource group of the deployed softwareUpdateConfiguration.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md new file mode 100644 index 0000000..2380819 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md @@ -0,0 +1,177 @@ +# Automation Account Software Update Configurations `[Microsoft.Automation/automationAccounts/softwareUpdateConfigurations]` + +This module deploys an Azure Automation Account Software update Configuration. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/softwareUpdateConfigurations` | [2019-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/softwareUpdateConfigurations) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `frequency` | string | `[Day, Hour, Month, OneTime, Week]` | The frequency of the deployment schedule. When using 'Hour', 'Day', 'Week' or 'Month', an interval needs to be provided. | +| `name` | string | | The name of the Deployment schedule. | +| `operatingSystem` | string | `[Linux, Windows]` | The operating system to be configured by the deployment schedule. | +| `rebootSetting` | string | `[Always, IfRequired, Never, RebootOnly]` | Reboot setting for the deployment schedule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `azureVirtualMachines` | array | `[]` | | List of azure resource IDs for azure virtual machines in scope for the deployment schedule. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `excludeUpdates` | array | `[]` | | KB numbers or Linux packages excluded in the deployment schedule. | +| `expiryTime` | string | `''` | | The end time of the deployment schedule in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. | +| `expiryTimeOffsetMinutes` | int | `0` | | The expiry time's offset in minutes. | +| `includeUpdates` | array | `[]` | | KB numbers or Linux packages included in the deployment schedule. | +| `interval` | int | `1` | | The interval of the frequency for the deployment schedule. 1 Hour is every hour, 2 Day is every second day, etc. | +| `isEnabled` | bool | `True` | | Enables the deployment schedule. | +| `maintenanceWindow` | string | `'PT2H'` | | Maximum time allowed for the deployment schedule to run. Duration needs to be specified using the format PT[n]H[n]M[n]S as per ISO8601. | +| `monthDays` | array | `[]` | `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]` | Can be used with frequency 'Month'. Provides the specific days of the month to run the deployment schedule. | +| `monthlyOccurrences` | array | `[]` | | Can be used with frequency 'Month'. Provides the pattern/cadence for running the deployment schedule in a month. Takes objects formed like this {occurance(int),day(string)}. Day is the name of the day to run the deployment schedule, the occurance specifies which occurance of that day to run the deployment schedule. | +| `nextRun` | string | `''` | | The next time the deployment schedule runs in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. | +| `nextRunOffsetMinutes` | int | `0` | | The next run's offset in minutes. | +| `nonAzureComputerNames` | array | `[]` | | List of names of non-azure machines in scope for the deployment schedule. | +| `nonAzureQueries` | array | `[]` | | Array of functions from a Log Analytics workspace, used to scope the deployment schedule. | +| `postTaskParameters` | object | `{object}` | | Parameters provided to the task running after the deployment schedule. | +| `postTaskSource` | string | `''` | | The source of the task running after the deployment schedule. | +| `preTaskParameters` | object | `{object}` | | Parameters provided to the task running before the deployment schedule. | +| `preTaskSource` | string | `''` | | The source of the task running before the deployment schedule. | +| `scheduleDescription` | string | `''` | | The schedules description. | +| `scopeByLocations` | array | `[]` | | Specify locations to which to scope the deployment schedule to. | +| `scopeByResources` | array | `[[subscription().id]]` | | Specify the resources to scope the deployment schedule to. | +| `scopeByTags` | object | `{object}` | | Specify tags to which to scope the deployment schedule to. | +| `scopeByTagsOperation` | string | `'All'` | `[All, Any]` | Enables the scopeByTags to require All (Tag A and Tag B) or Any (Tag A or Tag B). | +| `startTime` | string | `''` | | The start time of the deployment schedule in ISO 8601 format. To specify a specific time use YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. For schedules where we want to start the deployment as soon as possible, specify the time segment only in 24 hour format, HH:MM, 22:00. | +| `timeZone` | string | `'UTC'` | | Time zone for the deployment schedule. IANA ID or a Windows Time Zone ID. | +| `updateClassifications` | array | `[Critical, Security]` | `[Critical, Definition, FeaturePack, Other, Security, ServicePack, Tools, UpdateRollup, Updates]` | Update classification included in the deployment schedule. | +| `weekDays` | array | `[]` | `[Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday]` | Required when used with frequency 'Week'. Specified the day of the week to run the deployment schedule. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Do not touch. Is used to provide the base time for time comparison for startTime. If startTime is specified in HH:MM format, baseTime is used to check if the provided startTime has passed, adding one day before setting the deployment schedule. | + + +### Parameter Usage: `scopeByTags` + +Provide tag keys, with an array of values, filtering in machines that should be included in the deployment schedule. + +| Property name | Type | Possible values | Description | +| :------------ | :---- | :-------------- | :---------- | +| \ | array | string | tag values | + + +

+ +Parameter JSON format + +```json +"scopeByTags": { + "value": { + "Update": [ + "Automatic" + ], + "MaintenanceWindow": [ + "1-Sat-22" + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +scopeByTags: { + Update: [ + 'Automatic' + ] + MaintenanceWindow: [ + '1-Sat-22' + ] +} +``` + +
+

+ +### Parameter Usage: `monthlyOccurrences` + +Occurrences of days within a month. + +| Property name | Type | Possible values | Description | +| :------------ | :----- | :------------------------------------------------------------- | :----------------------------------------------------------------------------------- | +| `occurance` | int | 1-5 | Occurrence of the week within the month. Must be between 1 and 5, where 5 is "last". | +| `day` | string | Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday | Day of the occurrence. | + +

+ +Parameter JSON format + +```json +"monthlyOccurrences": { + "value": [ + { + "occurrence": 1, + "day": "Monday" + }, + { + "occurrence": 2, + "day": "Friday" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +monthlyOccurrences: [ + { + occurrence: 1 + day: 'Monday' + } + { + occurrence: 2 + day: 'Friday' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed softwareUpdateConfiguration. | +| `resourceGroupName` | string | The resource group of the deployed softwareUpdateConfiguration. | +| `resourceId` | string | The resource ID of the deployed softwareUpdateConfiguration. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/variables/deploy.bicep b/modules/Microsoft.Automation/automationAccounts/variables/deploy.bicep new file mode 100644 index 0000000..5b94ca5 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/variables/deploy.bicep @@ -0,0 +1,52 @@ +@sys.description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@sys.description('Required. The name of the variable.') +param name string + +@sys.description('Required. The value of the variable.') +param value string + +@sys.description('Optional. The description of the variable.') +param description string = '' + +@sys.description('Optional. If the variable should be encrypted. For security reasons encryption of variables should be enabled.') +param isEncrypted bool = true + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2021-06-22' existing = { + name: automationAccountName +} + +resource variable 'Microsoft.Automation/automationAccounts/variables@2020-01-13-preview' = { + name: name + parent: automationAccount + properties: { + description: description + isEncrypted: isEncrypted + value: value + } +} + +@sys.description('The name of the deployed variable.') +output name string = variable.name + +@sys.description('The resource ID of the deployed variable.') +output resourceId string = variable.id + +@sys.description('The resource group of the deployed variable.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Automation/automationAccounts/variables/readme.md b/modules/Microsoft.Automation/automationAccounts/variables/readme.md new file mode 100644 index 0000000..917c4d1 --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/variables/readme.md @@ -0,0 +1,100 @@ +# Automation Account Variables `[Microsoft.Automation/automationAccounts/variables]` + +This module deploys a variable to an Azure Automation Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/variables` | [2020-01-13-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Automation/2020-01-13-preview/automationAccounts/variables) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the variable. | +| `value` | string | The value of the variable. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description of the variable. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `isEncrypted` | bool | `True` | If the variable should be encrypted. For security reasons encryption of variables should be enabled. | + + +### Parameter Usage: `value` + +

+ +Parameter JSON format + +```json +//Boolean format +"value": { + "value": "false" +} + +//DateTime format +"value": { + "value": "\"\\/Date(1637934042656)\\/\"" +} + +//Integer format +"value": { + "value": "500" +} + +//String format +"value": { + "value": "\"TestString\"" +} +``` + +
+ +
+ +Bicep format + +```bicep +//Boolean format +value: 'false' + +//DateTime format +value: '\'\\/Date(1637934042656)\\/\'' + +//Integer format +value: '500' + +//String format +value: '\'TestString\'' +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed variable. | +| `resourceGroupName` | string | The resource group of the deployed variable. | +| `resourceId` | string | The resource ID of the deployed variable. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Automation/automationAccounts/variables/version.json b/modules/Microsoft.Automation/automationAccounts/variables/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/variables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Automation/automationAccounts/version.json b/modules/Microsoft.Automation/automationAccounts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Automation/automationAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Batch/batchAccounts/.test/encr.parameters.json b/modules/Microsoft.Batch/batchAccounts/.test/encr.parameters.json new file mode 100644 index 0000000..b930caf --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/.test/encr.parameters.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azbaweuencr001" + }, + "poolAllocationMode": { + "value": "BatchService" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "storageAuthenticationMode": { + "value": "BatchAccountManagedIdentity" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "storageAccessIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "batchAccount", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Batch/batchAccounts/.test/min.parameters.json b/modules/Microsoft.Batch/batchAccounts/.test/min.parameters.json new file mode 100644 index 0000000..5528a0d --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azbaweumin001" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + } +} diff --git a/modules/Microsoft.Batch/batchAccounts/.test/parameters.json b/modules/Microsoft.Batch/batchAccounts/.test/parameters.json new file mode 100644 index 0000000..fa1eb5f --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/.test/parameters.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azbaweux001" + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "batchAccount", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com" + ] + } + } + ] + }, + "networkProfileAllowedIpRanges": { + "value": [ + "127.0.0.1" + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "poolAllocationMode": { + "value": "BatchService" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "systemAssignedIdentity": { + "value": true + }, + "storageAuthenticationMode": { + "value": "BatchAccountManagedIdentity" + }, + "storageAccessIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + } + } +} diff --git a/modules/Microsoft.Batch/batchAccounts/deploy.bicep b/modules/Microsoft.Batch/batchAccounts/deploy.bicep new file mode 100644 index 0000000..d88a76d --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/deploy.bicep @@ -0,0 +1,270 @@ +@description('Required. Name of the Azure Batch.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Required. The resource ID of the storage account to be used for auto-storage account.') +param storageAccountId string + +@allowed([ + 'BatchAccountManagedIdentity' + 'StorageKeys' +]) +@description('Optional. The authentication mode which the Batch service will use to manage the auto-storage account.') +param storageAuthenticationMode string = 'StorageKeys' + +@description('Optional. The resource ID of a user assigned identity assigned to pools which have compute nodes that need access to auto-storage.') +param storageAccessIdentity string = '' + +@allowed([ + 'BatchService' + 'UserSubscription' +]) +@description('Optional. The allocation mode for creating pools in the Batch account. Determines which quota will be used.') +param poolAllocationMode string = 'BatchService' + +@description('Conditional. The key vault to associate with the Batch account. Required if the \'poolAllocationMode\' is set to \'UserSubscription\' and requires the service principal \'Microsoft Azure Batch\' to be granted contributor permissions on this key vault.') +param keyVaultReferenceResourceId string = '' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkProfileAllowedIpRanges are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@allowed([ + 'Allow' + 'Deny' +]) +@description('Optional. The network profile default action for endpoint access. It is only applicable when publicNetworkAccess is not explicitly disabled.') +param networkProfileDefaultAction string = 'Deny' + +@description('Optional. Array of IP ranges to filter client IP address. It is only applicable when publicNetworkAccess is not explicitly disabled.') +param networkProfileAllowedIpRanges array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + 'AAD' + 'SharedKey' + 'TaskAuthenticationToken' +]) +@description('Optional. List of allowed authentication modes for the Batch account that can be used to authenticate with the data plane.') +param allowedAuthenticationModes array = [] + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ServiceLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ServiceLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +var networkProfileIpRules = [for networkProfileAllowedIpRange in networkProfileAllowedIpRanges: { + action: 'Allow' + value: networkProfileAllowedIpRange +}] + +var nodeIdentityReference = !empty(storageAccessIdentity) ? { + resourceId: !empty(storageAccessIdentity) ? storageAccessIdentity : null +} : null + +var autoStorageConfig = { + authenticationMode: storageAuthenticationMode + nodeIdentityReference: nodeIdentityReference + storageAccountId: storageAccountId +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVaultReferenceKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(keyVaultReferenceResourceId)) { + name: last(split(keyVaultReferenceResourceId, '/')) + scope: resourceGroup(split(keyVaultReferenceResourceId, '/')[2], split(keyVaultReferenceResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource batchAccount 'Microsoft.Batch/batchAccounts@2022-06-01' = { + name: name + location: location + tags: tags + identity: identity + properties: { + allowedAuthenticationModes: allowedAuthenticationModes + autoStorage: autoStorageConfig + encryption: !empty(cMKKeyName) ? { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: { + keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVaultKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVaultKey.properties.keyUriWithVersion + } + } : null + keyVaultReference: poolAllocationMode == 'UserSubscription' ? { + id: keyVaultReferenceResourceId + url: keyVaultReferenceKeyVault.properties.vaultUri + } : null + networkProfile: (publicNetworkAccess == 'Disabled') || empty(networkProfileAllowedIpRanges) ? null : { + accountAccess: { + defaultAction: networkProfileDefaultAction + ipRules: networkProfileIpRules + } + } + poolAllocationMode: poolAllocationMode + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkProfileAllowedIpRanges) ? 'Disabled' : null) + } +} + +resource batchAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${batchAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: batchAccount +} + +resource batchAccount_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: batchAccount +} + +module batchAccount_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-BatchAccount-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(batchAccount.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: batchAccount.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The name of the batch account.') +output name string = batchAccount.name + +@description('The resource ID of the batch account.') +output resourceId string = batchAccount.id + +@description('The resource group the batch account was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = batchAccount.location diff --git a/modules/Microsoft.Batch/batchAccounts/readme.md b/modules/Microsoft.Batch/batchAccounts/readme.md new file mode 100644 index 0000000..873632f --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/readme.md @@ -0,0 +1,499 @@ +# Batch Accounts `[Microsoft.Batch/batchAccounts]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Batch/batchAccounts` | [2022-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Batch/2022-06-01/batchAccounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Batch. | +| `storageAccountId` | string | The resource ID of the storage account to be used for auto-storage account. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `keyVaultReferenceResourceId` | string | `''` | The key vault to associate with the Batch account. Required if the 'poolAllocationMode' is set to 'UserSubscription' and requires the service principal 'Microsoft Azure Batch' to be granted contributor permissions on this key vault. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowedAuthenticationModes` | array | `[]` | `[AAD, SharedKey, TaskAuthenticationToken]` | List of allowed authentication modes for the Batch account that can be used to authenticate with the data plane. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ServiceLog]` | `[ServiceLog]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkProfileAllowedIpRanges` | array | `[]` | | Array of IP ranges to filter client IP address. It is only applicable when publicNetworkAccess is not explicitly disabled. | +| `networkProfileDefaultAction` | string | `'Deny'` | `[Allow, Deny]` | The network profile default action for endpoint access. It is only applicable when publicNetworkAccess is not explicitly disabled. | +| `poolAllocationMode` | string | `'BatchService'` | `[BatchService, UserSubscription]` | The allocation mode for creating pools in the Batch account. Determines which quota will be used. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkProfileAllowedIpRanges are not set. | +| `storageAccessIdentity` | string | `''` | | The resource ID of a user assigned identity assigned to pools which have compute nodes that need access to auto-storage. | +| `storageAuthenticationMode` | string | `'StorageKeys'` | `[BatchAccountManagedIdentity, StorageKeys]` | The authentication mode which the Batch service will use to manage the auto-storage account. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the batch account. | +| `resourceGroupName` | string | The resource group the batch account was deployed into. | +| `resourceId` | string | The resource ID of the batch account. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module batchAccounts './Microsoft.Batch/batchAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BatchAccounts' + params: { + // Required parameters + name: '<>azbaweuencr001' + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + poolAllocationMode: 'BatchService' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com' + ] + } + service: 'batchAccount' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + storageAccessIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + storageAuthenticationMode: 'BatchAccountManagedIdentity' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>azbaweuencr001" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "poolAllocationMode": { + "value": "BatchService" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com" + ] + }, + "service": "batchAccount", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "storageAccessIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "storageAuthenticationMode": { + "value": "BatchAccountManagedIdentity" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module batchAccounts './Microsoft.Batch/batchAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BatchAccounts' + params: { + // Required parameters + name: '<>azbaweumin001' + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + } +} +``` + +
+

+ +

+ +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": "<>azbaweumin001" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module batchAccounts './Microsoft.Batch/batchAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BatchAccounts' + params: { + // Required parameters + name: '<>azbaweux001' + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + networkProfileAllowedIpRanges: [ + '127.0.0.1' + ] + poolAllocationMode: 'BatchService' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com' + ] + } + service: 'batchAccount' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + storageAccessIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + storageAuthenticationMode: 'BatchAccountManagedIdentity' + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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": "<>azbaweux001" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkProfileAllowedIpRanges": { + "value": [ + "127.0.0.1" + ] + }, + "poolAllocationMode": { + "value": "BatchService" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.batch.azure.com" + ] + }, + "service": "batchAccount", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "storageAccessIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "storageAuthenticationMode": { + "value": "BatchAccountManagedIdentity" + }, + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Batch/batchAccounts/version.json b/modules/Microsoft.Batch/batchAccounts/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Batch/batchAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Cache/redis/.bicep/nested_rbac.bicep b/modules/Microsoft.Cache/redis/.bicep/nested_rbac.bicep new file mode 100644 index 0000000..6402510 --- /dev/null +++ b/modules/Microsoft.Cache/redis/.bicep/nested_rbac.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + '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 redisCache 'Microsoft.Cache/redis@2021-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(redisCache.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: redisCache +}] diff --git a/modules/Microsoft.Cache/redis/.test/min.parameters.json b/modules/Microsoft.Cache/redis/.test/min.parameters.json new file mode 100644 index 0000000..1c7bb6e --- /dev/null +++ b/modules/Microsoft.Cache/redis/.test/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": "<>-az-redis-min-001" + } + } +} diff --git a/modules/Microsoft.Cache/redis/.test/parameters.json b/modules/Microsoft.Cache/redis/.test/parameters.json new file mode 100644 index 0000000..f9b301c --- /dev/null +++ b/modules/Microsoft.Cache/redis/.test/parameters.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-redis-full-001" + }, + "capacity": { + "value": 2 + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "ApplicationGatewayAccessLog", + "ApplicationGatewayFirewallLog" + ] + }, + "diagnosticMetricsToEnable": { + "value": [ + "AllMetrics" + ] + }, + "enableNonSslPort": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "minimumTlsVersion": { + "value": "1.2" + }, + "diagnosticSettingsName": { + "value": "redisdiagnostics" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "redisVersion": { + "value": "6" + }, + "skuName": { + "value": "Premium" + }, + "systemAssignedIdentity": { + "value": true + }, + "shardCount": { + "value": 1 + }, + "tags": { + "value": { + "resourceType": "Redis Cache" + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "redisCache", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.redis.cache.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Cache/redis/deploy.bicep b/modules/Microsoft.Cache/redis/deploy.bicep new file mode 100644 index 0000000..2278ab6 --- /dev/null +++ b/modules/Microsoft.Cache/redis/deploy.bicep @@ -0,0 +1,283 @@ +@description('Optional. The location to deploy the Redis cache service.') +param location string = resourceGroup().location + +@description('Required. The name of the Redis cache resource.') +param name string + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Specifies whether the non-ssl Redis server port (6379) is enabled.') +param enableNonSslPort bool = false + +@allowed([ + '1.0' + '1.1' + '1.2' +]) +@description('Optional. Requires clients to use a specified TLS version (or higher) to connect.') +param minimumTlsVersion string = '1.2' + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. All Redis Settings. Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value etc.') +param redisConfiguration object = {} + +@allowed([ + '4' + '6' +]) +@description('Optional. Redis version. Only major version will be used in PUT/PATCH request with current valid values: (4, 6).') +param redisVersion string = '6' + +@minValue(1) +@description('Optional. The number of replicas to be created per primary.') +param replicasPerMaster int = 1 + +@minValue(1) +@description('Optional. The number of replicas to be created per primary.') +param replicasPerPrimary int = 1 + +@minValue(1) +@description('Optional. The number of shards to be created on a Premium Cluster Cache.') +param shardCount int = 1 + +@allowed([ + 0 + 1 + 2 + 3 + 4 + 5 + 6 +]) +@description('Optional. The size of the Redis cache to deploy. Valid values: for C (Basic/Standard) family (0, 1, 2, 3, 4, 5, 6), for P (Premium) family (1, 2, 3, 4).') +param capacity int = 1 + +@allowed([ + 'Basic' + 'Premium' + 'Standard' +]) +@description('Optional. The type of Redis cache to deploy.') +param skuName string = 'Basic' + +@description('Optional. Static IP address. Optionally, may be specified when deploying a Redis cache inside an existing Azure Virtual Network; auto assigned by default.') +param staticIP string = '' + +@description('Optional. The full resource ID of a subnet in a virtual network to deploy the Redis cache in. Example format: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/Microsoft.{Network|ClassicNetwork}/VirtualNetworks/vnet1/subnets/subnet1.') +param subnetId string = '' + +@description('Optional. A dictionary of tenant settings.') +param tenantSettings object = {} + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayPerformanceLog' + 'ApplicationGatewayFirewallLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayPerformanceLog' + 'ApplicationGatewayFirewallLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource redisCache 'Microsoft.Cache/redis@2021-06-01' = { + name: name + location: location + tags: tags + identity: identity + properties: { + enableNonSslPort: enableNonSslPort + minimumTlsVersion: minimumTlsVersion + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : null) + redisConfiguration: !empty(redisConfiguration) ? redisConfiguration : null + redisVersion: redisVersion + replicasPerMaster: skuName == 'Premium' ? replicasPerMaster : null + replicasPerPrimary: skuName == 'Premium' ? replicasPerPrimary : null + shardCount: skuName == 'Premium' ? shardCount : null // Not supported in free tier + sku: { + capacity: capacity + family: skuName == 'Premium' ? 'P' : 'C' + name: skuName + } + staticIP: !empty(staticIP) ? staticIP : null + subnetId: !empty(subnetId) ? subnetId : null + tenantSettings: tenantSettings + } + zones: skuName == 'Premium' ? pickZones('Microsoft.Cache', 'redis', location, 1) : null +} + +resource redisCache_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${redisCache.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: redisCache +} + +resource redisCache_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId + workspaceId: empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId + eventHubAuthorizationRuleId: empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId + eventHubName: empty(diagnosticEventHubName) ? null : diagnosticEventHubName + metrics: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsMetrics + logs: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsLogs + } + scope: redisCache +} + +module redisCache_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: redisCache.id + } +}] + +module redisCache_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-redisCache-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(redisCache.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: redisCache.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The resource name.') +output name string = redisCache.name + +@description('The resource ID.') +output resourceId string = redisCache.id + +@description('The name of the resource group the Redis cache was created in.') +output resourceGroupName string = resourceGroup().name + +@description('Redis hostname.') +output hostName string = redisCache.properties.hostName + +@description('Redis SSL port.') +output sslPort int = redisCache.properties.sslPort + +@description('The full resource ID of a subnet in a virtual network where the Redis cache was deployed in.') +output subnetId string = !empty(subnetId) ? redisCache.properties.subnetId : '' + +@description('The location the resource was deployed into.') +output location string = redisCache.location diff --git a/modules/Microsoft.Cache/redis/readme.md b/modules/Microsoft.Cache/redis/readme.md new file mode 100644 index 0000000..94cc75d --- /dev/null +++ b/modules/Microsoft.Cache/redis/readme.md @@ -0,0 +1,505 @@ +# Cache Redis `[Microsoft.Cache/redis]` + +This module deploys a Redis Cache service. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Cache/redis` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Cache/2021-06-01/redis) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Redis cache resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `capacity` | int | `1` | `[0, 1, 2, 3, 4, 5, 6]` | The size of the Redis cache to deploy. Valid values: for C (Basic/Standard) family (0, 1, 2, 3, 4, 5, 6), for P (Premium) family (1, 2, 3, 4). | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticLogCategoriesToEnable` | array | `[ApplicationGatewayAccessLog, ApplicationGatewayFirewallLog, ApplicationGatewayPerformanceLog]` | `[ApplicationGatewayAccessLog, ApplicationGatewayFirewallLog, ApplicationGatewayPerformanceLog]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableNonSslPort` | bool | `False` | | Specifies whether the non-ssl Redis server port (6379) is enabled. | +| `location` | string | `[resourceGroup().location]` | | The location to deploy the Redis cache service. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `minimumTlsVersion` | string | `'1.2'` | `[1.0, 1.1, 1.2]` | Requires clients to use a specified TLS version (or higher) to connect. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `redisConfiguration` | object | `{object}` | | All Redis Settings. Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value etc. | +| `redisVersion` | string | `'6'` | `[4, 6]` | Redis version. Only major version will be used in PUT/PATCH request with current valid values: (4, 6). | +| `replicasPerMaster` | int | `1` | | The number of replicas to be created per primary. | +| `replicasPerPrimary` | int | `1` | | The number of replicas to be created per primary. | +| `roleAssignments` | array | `[]` | | 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'. | +| `shardCount` | int | `1` | | The number of shards to be created on a Premium Cluster Cache. | +| `skuName` | string | `'Basic'` | `[Basic, Premium, Standard]` | The type of Redis cache to deploy. | +| `staticIP` | string | `''` | | Static IP address. Optionally, may be specified when deploying a Redis cache inside an existing Azure Virtual Network; auto assigned by default. | +| `subnetId` | string | `''` | | The full resource ID of a subnet in a virtual network to deploy the Redis cache in. Example format: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/Microsoft.{Network|ClassicNetwork}/VirtualNetworks/vnet1/subnets/subnet1. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `tenantSettings` | object | `{object}` | | A dictionary of tenant settings. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `redisConfiguration` + +All Redis Settings. Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value etc.. + +Name | Description | Value +---------|----------|--------- +aof-storage-connection-string-0 | First storage account connection string | string +aof-storage-connection-string-1 | Second storage account connection string | string +maxfragmentationmemory-reserved | Value in megabytes reserved for fragmentation per shard | string +maxmemory-delta | Value in megabytes reserved for non-cache usage per shard e.g. failover. | string +maxmemory-policy | The eviction strategy used when your data won't fit within its memory limit. | string +maxmemory-reserved | Value in megabytes reserved for non-cache usage per shard e.g. failover. | string +rdb-backup-enabled | Specifies whether the rdb backup is enabled | string +rdb-backup-frequency | Specifies the frequency for creating rdb backup | string +rdb-backup-max-snapshot-count | Specifies the maximum number of snapshots for rdb backup | string +rdb-storage-connection-string | The storage account connection string for storing rdb file | string + +For more details visit [Microsoft.Cache redis reference](https://docs.microsoft.com/en-us/azure/templates/microsoft.cache/redis?tabs=bicep) + +

+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `hostName` | string | Redis hostname. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The resource name. | +| `resourceGroupName` | string | The name of the resource group the Redis cache was created in. | +| `resourceId` | string | The resource ID. | +| `sslPort` | int | Redis SSL port. | +| `subnetId` | string | The full resource ID of a subnet in a virtual network where the Redis cache was deployed in. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module redis './Microsoft.Cache/redis/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Redis' + params: { + name: '<>-az-redis-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-redis-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module redis './Microsoft.Cache/redis/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Redis' + params: { + // Required parameters + name: '<>-az-redis-full-001' + // Non-required parameters + capacity: 2 + diagnosticLogCategoriesToEnable: [ + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayFirewallLog' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticSettingsName: 'redisdiagnostics' + enableNonSslPort: true + lock: 'CanNotDelete' + minimumTlsVersion: '1.2' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.redis.cache.windows.net' + ] + } + service: 'redisCache' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + publicNetworkAccess: 'Enabled' + redisVersion: '6' + shardCount: 1 + skuName: 'Premium' + systemAssignedIdentity: true + tags: { + resourceType: 'Redis Cache' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-redis-full-001" + }, + // Non-required parameters + "capacity": { + "value": 2 + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "ApplicationGatewayAccessLog", + "ApplicationGatewayFirewallLog" + ] + }, + "diagnosticMetricsToEnable": { + "value": [ + "AllMetrics" + ] + }, + "diagnosticSettingsName": { + "value": "redisdiagnostics" + }, + "enableNonSslPort": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "minimumTlsVersion": { + "value": "1.2" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.redis.cache.windows.net" + ] + }, + "service": "redisCache", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "redisVersion": { + "value": "6" + }, + "shardCount": { + "value": 1 + }, + "skuName": { + "value": "Premium" + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "resourceType": "Redis Cache" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Cache/redis/version.json b/modules/Microsoft.Cache/redis/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Cache/redis/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.CognitiveServices/accounts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.CognitiveServices/accounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d0dc97e --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,85 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services Custom Vision Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3') + 'Cognitive Services Custom Vision Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f') + 'Cognitive Services Custom Vision Labeler': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c') + 'Cognitive Services Custom Vision Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73') + 'Cognitive Services Custom Vision Trainer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b') + 'Cognitive Services Data Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c') + 'Cognitive Services Face Recognizer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7') + 'Cognitive Services Metrics Advisor Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a') + 'Cognitive Services Metrics Advisor User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8') + 'Cognitive Services QnA Maker Editor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025') + 'Cognitive Services QnA Maker Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126') + 'Cognitive Services Speech Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181') + 'Cognitive Services Speech User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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 account 'Microsoft.CognitiveServices/accounts@2017-04-18' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(account.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: account +}] diff --git a/modules/Microsoft.CognitiveServices/accounts/.test/encr.parameters.json b/modules/Microsoft.CognitiveServices/accounts/.test/encr.parameters.json new file mode 100644 index 0000000..b003050 --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/.test/encr.parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cgs-encr-001" + }, + "kind": { + "value": "SpeechServices" + }, + "sku": { + "value": "S0" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "encryption": { + "value": { + "keySource": "Microsoft.KeyVault", + "keyVaultProperties": { + "identityClientId": "5d395e10-82b1-4c41-bb5b-a27757e9f725", // ID must be updated for new identity + "keyVaultUri": "https://adp-<>-az-kv-nopr-002.vault.azure.net/", + "keyName": "keyEncryptionKey", + "keyversion": "4570a207ec394a0bbbe4fc9adc663a51" // Version must be updated for new keys + } + } + } + } +} diff --git a/modules/Microsoft.CognitiveServices/accounts/.test/min.parameters.json b/modules/Microsoft.CognitiveServices/accounts/.test/min.parameters.json new file mode 100644 index 0000000..0f4f624 --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cgs-min-001" + }, + "kind": { + "value": "SpeechServices" + } + } +} diff --git a/modules/Microsoft.CognitiveServices/accounts/.test/parameters.json b/modules/Microsoft.CognitiveServices/accounts/.test/parameters.json new file mode 100644 index 0000000..1fd4474 --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/.test/parameters.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cgs-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "kind": { + "value": "Face" + }, + "sku": { + "value": "S0" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "networkAcls": { + "value": { + "defaultAction": "deny", + "virtualNetworkRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "action": "Allow" + } + ], + "ipRules": [ + { + "value": "40.74.28.0/23" + } + ] + } + }, + "customSubDomainName": { + "value": "<>xdomain" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "account", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com" + ] + } + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.CognitiveServices/accounts/.test/speech.parameters.json b/modules/Microsoft.CognitiveServices/accounts/.test/speech.parameters.json new file mode 100644 index 0000000..2f4d7ac --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/.test/speech.parameters.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cgs-speech-001" + }, + "kind": { + "value": "SpeechServices" + }, + "sku": { + "value": "S0" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "customSubDomainName": { + "value": "<>speechdomain" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "account", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.CognitiveServices/accounts/deploy.bicep b/modules/Microsoft.CognitiveServices/accounts/deploy.bicep new file mode 100644 index 0000000..72a8fca --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/deploy.bicep @@ -0,0 +1,293 @@ +@description('Required. The name of Cognitive Services account.') +param name string + +@description('Required. Kind of the Cognitive Services. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.') +@allowed([ + 'AnomalyDetector' + 'Bing.Autosuggest.v7' + 'Bing.CustomSearch' + 'Bing.EntitySearch' + 'Bing.Search.v7' + 'Bing.SpellCheck.v7' + 'CognitiveServices' + 'ComputerVision' + 'ContentModerator' + 'CustomVision.Prediction' + 'CustomVision.Training' + 'Face' + 'FormRecognizer' + 'ImmersiveReader' + 'Internal.AllInOne' + 'LUIS' + 'LUIS.Authoring' + 'Personalizer' + 'QnAMaker' + 'SpeechServices' + 'TextAnalytics' + 'TextTranslation' +]) +param kind string + +@description('Optional. SKU of the Cognitive Services resource. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.') +@allowed([ + 'C2' + 'C3' + 'C4' + 'F0' + 'F1' + 'S' + 'S0' + 'S1' + 'S10' + 'S2' + 'S3' + 'S4' + 'S5' + 'S6' + 'S7' + 'S8' + 'S9' +]) +param sku string = 'S0' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Conditional. Subdomain name used for token-based authentication. Required if \'networkAcls\' or \'privateEndpoints\' are set.') +param customSubDomainName string = '' + +@description('Optional. A collection of rules governing the accessibility from specific network locations.') +param networkAcls object = {} + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Conditional. The ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. List of allowed FQDN.') +param allowedFqdnList array = [] + +@description('Optional. The API properties for special APIs.') +param apiProperties object = {} + +@description('Optional. Allow only Azure AD authentication. Should be enabled for security reasons.') +param disableLocalAuth bool = true + +@description('Optional. Properties to configure encryption.') +param encryption object = {} + +@description('Optional. Resource migration token.') +param migrationToken string = '' + +@description('Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists.') +param restore bool = false + +@description('Optional. Restrict outbound network access.') +param restrictOutboundNetworkAccess bool = true + +@description('Optional. The storage accounts for this resource.') +param userOwnedStorage array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'Audit' + 'RequestResponse' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Audit' + 'RequestResponse' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cognitiveServices 'Microsoft.CognitiveServices/accounts@2021-10-01' = { + name: name + kind: kind + identity: identity + location: location + tags: tags + sku: { + name: sku + } + properties: { + customSubDomainName: !empty(customSubDomainName) ? customSubDomainName : null + networkAcls: networkAcls + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + allowedFqdnList: allowedFqdnList + apiProperties: apiProperties + disableLocalAuth: disableLocalAuth + encryption: !empty(encryption) ? encryption : null + migrationToken: !empty(migrationToken) ? migrationToken : null + restore: restore + restrictOutboundNetworkAccess: restrictOutboundNetworkAccess + userOwnedStorage: !empty(userOwnedStorage) ? userOwnedStorage : null + } +} + +resource cognitiveServices_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${cognitiveServices.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: cognitiveServices +} + +resource cognitiveServices_diagnosticSettingName 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: cognitiveServices +} + +module cognitiveServices_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-CognitiveServices-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(cognitiveServices.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: cognitiveServices.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module cognitiveServices_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-CognitiveServices-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: cognitiveServices.id + } +}] + +@description('The name of the cognitive services account.') +output name string = cognitiveServices.name + +@description('The resource ID of the cognitive services account.') +output resourceId string = cognitiveServices.id + +@description('The resource group the cognitive services account was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The service endpoint of the cognitive services account.') +output endpoint string = cognitiveServices.properties.endpoint + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(cognitiveServices.identity, 'principalId') ? cognitiveServices.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = cognitiveServices.location diff --git a/modules/Microsoft.CognitiveServices/accounts/readme.md b/modules/Microsoft.CognitiveServices/accounts/readme.md new file mode 100644 index 0000000..927de37 --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/readme.md @@ -0,0 +1,797 @@ +# Cognitive Services `[Microsoft.CognitiveServices/accounts]` + +This module deploys different kinds of cognitive services resources + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.CognitiveServices/accounts` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2021-10-01/accounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `kind` | string | `[AnomalyDetector, Bing.Autosuggest.v7, Bing.CustomSearch, Bing.EntitySearch, Bing.Search.v7, Bing.SpellCheck.v7, CognitiveServices, ComputerVision, ContentModerator, CustomVision.Prediction, CustomVision.Training, Face, FormRecognizer, ImmersiveReader, Internal.AllInOne, LUIS, LUIS.Authoring, Personalizer, QnAMaker, SpeechServices, TextAnalytics, TextTranslation]` | Kind of the Cognitive Services. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. | +| `name` | string | | The name of Cognitive Services account. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `customSubDomainName` | string | `''` | Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set. | +| `userAssignedIdentities` | object | `{object}` | The ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowedFqdnList` | array | `[]` | | List of allowed FQDN. | +| `apiProperties` | object | `{object}` | | The API properties for special APIs. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Audit, RequestResponse]` | `[Audit, RequestResponse]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableLocalAuth` | bool | `True` | | Allow only Azure AD authentication. Should be enabled for security reasons. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `encryption` | object | `{object}` | | Properties to configure encryption. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `migrationToken` | string | `''` | | Resource migration token. | +| `networkAcls` | object | `{object}` | | A collection of rules governing the accessibility from specific network locations. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. | +| `restore` | bool | `False` | | Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists. | +| `restrictOutboundNetworkAccess` | bool | `True` | | Restrict outbound network access. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'S0'` | `[C2, C3, C4, F0, F1, S, S0, S1, S10, S2, S3, S4, S5, S6, S7, S8, S9]` | SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userOwnedStorage` | array | `[]` | | The storage accounts for this 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `encryption` + +

+ +Parameter JSON format + +```json +// With customer-managed key +"encryption": { + "value": { + "keySource": "Microsoft.KeyVault", + "keyVaultProperties": { + "identityClientId": "c907a696-36f4-49fe-b926-39e3aabba814", // ID must be updated for new identity + "keyVaultUri": "https://adp-<>-az-kv-nopr-002.vault.azure.net/", + "keyName": "keyEncryptionKey", + "keyversion": "4570a207ec394a0bbbe4fc9adc663a51" // ID must be updated for new keys + } + } +} +// With service-managed key +"encryption": { + "value": { + "keySource": "Microsoft.CognitiveServices" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +// With customer managed key +encryption: { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: { + identityClientId: 'c907a696-36f4-49fe-b926-39e3aabba814' // ID must be updated for new identity + keyVaultUri: 'https://adp-<>-az-kv-nopr-002.vault.azure.net/' + keyName: 'keyEncryptionKey' + keyversion: '4570a207ec394a0bbbe4fc9adc663a51' // Version must be updated for new keys + } +} +// With service-managed key +encryption: { + keySource: 'Microsoft.CognitiveServices' +} +``` + +
+

+### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `networkAcls` + +

+ +Parameter JSON format + +```json +"networkAcls": { + "value": { + "defaultAction": "Deny", + "virtualNetworkRules": [ + { + "id": "/subscriptions//resourceGroups/resourceGroup/providers/Microsoft.Network/virtualNetworks//subnets/", + "ignoreMissingVnetServiceEndpoint": false + } + ], + "ipRules": [ + { + "value": "1.1.1.1" + }, + { + "value": "" + } + ] + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +networkAcls: { + defaultAction: 'Deny' + virtualNetworkRules: [ + { + id: '/subscriptions//resourceGroups/resourceGroup/providers/Microsoft.Network/virtualNetworks//subnets/' + ignoreMissingVnetServiceEndpoint: false + } + ] + ipRules: [ + { + value: '1.1.1.1' + } + { + value: '' + } + ] +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The service endpoint of the cognitive services account. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the cognitive services account. | +| `resourceGroupName` | string | The resource group the cognitive services account was deployed into. | +| `resourceId` | string | The resource ID of the cognitive services account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Considerations + +- Not all combinations of parameters `kind` and `SKU` are valid and they may vary in different Azure Regions. Please use PowerShell cmdlet `Get-AzCognitiveServicesAccountSku` or another methods to determine valid values in your region. +- Not all kinds of Cognitive Services support virtual networks. Please visit the link below to determine supported services. + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module accounts './Microsoft.CognitiveServices/accounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Accounts' + params: { + // Required parameters + kind: 'SpeechServices' + name: '<>-az-cgs-encr-001' + // Non-required parameters + encryption: { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: { + identityClientId: '5d395e10-82b1-4c41-bb5b-a27757e9f725' + keyName: 'keyEncryptionKey' + keyVaultUri: 'https://adp-<>-az-kv-nopr-002.vault.azure.net/' + keyversion: '4570a207ec394a0bbbe4fc9adc663a51' + } + } + publicNetworkAccess: 'Enabled' + sku: 'S0' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "SpeechServices" + }, + "name": { + "value": "<>-az-cgs-encr-001" + }, + // Non-required parameters + "encryption": { + "value": { + "keySource": "Microsoft.KeyVault", + "keyVaultProperties": { + "identityClientId": "5d395e10-82b1-4c41-bb5b-a27757e9f725", + "keyName": "keyEncryptionKey", + "keyVaultUri": "https://adp-<>-az-kv-nopr-002.vault.azure.net/", + "keyversion": "4570a207ec394a0bbbe4fc9adc663a51" + } + } + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "sku": { + "value": "S0" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module accounts './Microsoft.CognitiveServices/accounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Accounts' + params: { + // Required parameters + kind: 'SpeechServices' + name: '<>-az-cgs-min-001' + } +} +``` + +
+

+ +

+ +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": "SpeechServices" + }, + "name": { + "value": "<>-az-cgs-min-001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module accounts './Microsoft.CognitiveServices/accounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Accounts' + params: { + // Required parameters + kind: 'Face' + name: '<>-az-cgs-x-001' + // Non-required parameters + customSubDomainName: '<>xdomain' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + networkAcls: { + defaultAction: 'deny' + ipRules: [ + { + value: '40.74.28.0/23' + } + ] + virtualNetworkRules: [ + { + action: 'Allow' + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com' + ] + } + service: 'account' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'S0' + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "Face" + }, + "name": { + "value": "<>-az-cgs-x-001" + }, + // Non-required parameters + "customSubDomainName": { + "value": "<>xdomain" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "defaultAction": "deny", + "ipRules": [ + { + "value": "40.74.28.0/23" + } + ], + "virtualNetworkRules": [ + { + "action": "Allow", + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com" + ] + }, + "service": "account", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "S0" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 4: Speech

+ +
+ +via Bicep module + +```bicep +module accounts './Microsoft.CognitiveServices/accounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Accounts' + params: { + // Required parameters + kind: 'SpeechServices' + name: '<>-az-cgs-speech-001' + // Non-required parameters + customSubDomainName: '<>speechdomain' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com' + ] + } + service: 'account' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + sku: 'S0' + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "SpeechServices" + }, + "name": { + "value": "<>-az-cgs-speech-001" + }, + // Non-required parameters + "customSubDomainName": { + "value": "<>speechdomain" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com" + ] + }, + "service": "account", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "sku": { + "value": "S0" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.CognitiveServices/accounts/version.json b/modules/Microsoft.CognitiveServices/accounts/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.CognitiveServices/accounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Compute/availabilitySets/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/availabilitySets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..deeae1c --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource availabilitySet 'Microsoft.Compute/availabilitySets@2021-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(availabilitySet.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: availabilitySet +}] diff --git a/modules/Microsoft.Compute/availabilitySets/.test/min.parameters.json b/modules/Microsoft.Compute/availabilitySets/.test/min.parameters.json new file mode 100644 index 0000000..99d2414 --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/.test/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": "<>-az-avs-min-001" + } + } +} diff --git a/modules/Microsoft.Compute/availabilitySets/.test/parameters.json b/modules/Microsoft.Compute/availabilitySets/.test/parameters.json new file mode 100644 index 0000000..f7d8be5 --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/.test/parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avs-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "proximityPlacementGroupId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-x-001" + } + } +} diff --git a/modules/Microsoft.Compute/availabilitySets/deploy.bicep b/modules/Microsoft.Compute/availabilitySets/deploy.bicep new file mode 100644 index 0000000..18dd312 --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/deploy.bicep @@ -0,0 +1,99 @@ +@description('Required. The name of the availability set that is being created.') +param name string + +@description('Optional. The number of fault domains to use.') +param availabilitySetFaultDomain int = 2 + +@description('Optional. The number of update domains to use.') +param availabilitySetUpdateDomain int = 5 + +@description('''Optional. SKU of the availability set. +- Use \'Aligned\' for virtual machines with managed disks. +- Use \'Classic\' for virtual machines with unmanaged disks. +''') +param availabilitySetSku string = 'Aligned' + +@description('Optional. Resource ID of a proximity placement group.') +param proximityPlacementGroupId string = '' + +@description('Optional. Resource location.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the availability set resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource availabilitySet 'Microsoft.Compute/availabilitySets@2021-07-01' = { + name: name + location: location + tags: tags + properties: { + platformFaultDomainCount: availabilitySetFaultDomain + platformUpdateDomainCount: availabilitySetUpdateDomain + proximityPlacementGroup: !empty(proximityPlacementGroupId) ? { + id: proximityPlacementGroupId + } : null + } + sku: { + name: availabilitySetSku + } +} + +resource availabilitySet_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${availabilitySet.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: availabilitySet +} + +module availabilitySet_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AvSet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: availabilitySet.id + } +}] + +@description('The name of the availability set.') +output name string = availabilitySet.name + +@description('The resource ID of the availability set.') +output resourceId string = availabilitySet.id + +@description('The resource group the availability set was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = availabilitySet.location diff --git a/modules/Microsoft.Compute/availabilitySets/readme.md b/modules/Microsoft.Compute/availabilitySets/readme.md new file mode 100644 index 0000000..57d35f2 --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/readme.md @@ -0,0 +1,264 @@ +# Availability Sets `[Microsoft.Compute/availabilitySets]` + +This template deploys an availability set + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/availabilitySets` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/availabilitySets) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the availability set that is being created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `availabilitySetFaultDomain` | int | `2` | | The number of fault domains to use. | +| `availabilitySetSku` | string | `'Aligned'` | | SKU of the availability set.

- Use \'Aligned\' for virtual machines with managed disks.

- Use \'Classic\' for virtual machines with unmanaged disks.

| +| `availabilitySetUpdateDomain` | int | `5` | | The number of update domains to use. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Resource location. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `proximityPlacementGroupId` | string | `''` | | Resource ID of a proximity placement group. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the availability set resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the availability set. | +| `resourceGroupName` | string | The resource group the availability set was deployed into. | +| `resourceId` | string | The resource ID of the availability set. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module availabilitySets './Microsoft.Compute/availabilitySets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AvailabilitySets' + params: { + name: '<>-az-avs-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avs-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module availabilitySets './Microsoft.Compute/availabilitySets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AvailabilitySets' + params: { + // Required parameters + name: '<>-az-avs-x-001' + // Non-required parameters + lock: 'CanNotDelete' + proximityPlacementGroupId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-avs-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "proximityPlacementGroupId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-x-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/availabilitySets/version.json b/modules/Microsoft.Compute/availabilitySets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/availabilitySets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/diskEncryptionSets/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/diskEncryptionSets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..5cdc604 --- /dev/null +++ b/modules/Microsoft.Compute/diskEncryptionSets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,74 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + '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 diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2020-12-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(diskEncryptionSet.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: diskEncryptionSet +}] diff --git a/modules/Microsoft.Compute/diskEncryptionSets/.test/parameters.json b/modules/Microsoft.Compute/diskEncryptionSets/.test/parameters.json new file mode 100644 index 0000000..58ec4d9 --- /dev/null +++ b/modules/Microsoft.Compute/diskEncryptionSets/.test/parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-des-x-001" + }, + "keyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "keyName": { + "value": "keyEncryptionKey" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/diskEncryptionSets/deploy.bicep b/modules/Microsoft.Compute/diskEncryptionSets/deploy.bicep new file mode 100644 index 0000000..8aface6 --- /dev/null +++ b/modules/Microsoft.Compute/diskEncryptionSets/deploy.bicep @@ -0,0 +1,124 @@ +@description('Required. The name of the disk encryption set that is being created.') +param name string + +@description('Optional. Resource location.') +param location string = resourceGroup().location + +@description('Required. Resource ID of the KeyVault containing the key or secret.') +param keyVaultResourceId string + +@description('Required. Key URL (with version) pointing to a key or secret in KeyVault.') +param keyName string + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param keyVersion string = '' + +@description('Optional. The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys.') +@allowed([ + 'EncryptionAtRestWithCustomerKey' + 'EncryptionAtRestWithPlatformAndCustomerKeys' +]) +param encryptionType string = 'EncryptionAtRestWithPlatformAndCustomerKeys' + +@description('Optional. Set this flag to true to enable auto-updating of this disk encryption set to the latest key version.') +param rotationToLatestKeyVersionEnabled bool = false + +@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. Tags of the disk encryption resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = { + name: '${last(split(keyVaultResourceId, '/'))}/${keyName}' + scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4]) +} + +resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2021-04-01' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + activeKey: { + sourceVault: { + id: keyVaultResourceId + } + keyUrl: !empty(keyVersion) ? '${keyVaultKey.properties.keyUri}/${keyVersion}' : keyVaultKey.properties.keyUriWithVersion + } + encryptionType: encryptionType + rotationToLatestKeyVersionEnabled: rotationToLatestKeyVersionEnabled + } +} + +module keyVaultAccessPolicies '../../Microsoft.KeyVault/vaults/accessPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-DiskEncrSet-KVAccessPolicies' + params: { + keyVaultName: last(split(keyVaultResourceId, '/')) + accessPolicies: [ + { + tenantId: subscription().tenantId + objectId: diskEncryptionSet.identity.principalId + permissions: { + keys: [ + 'get' + 'wrapKey' + 'unwrapKey' + ] + secrets: [] + certificates: [] + } + } + ] + } + // This is to support access policies to KV in different subscription and resource group than the disk encryption set. + scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4]) +} + +module diskEncryptionSet_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-DiskEncrSet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: diskEncryptionSet.id + } +}] + +@description('The resource ID of the disk encryption set.') +output resourceId string = diskEncryptionSet.id + +@description('The name of the disk encryption set.') +output name string = diskEncryptionSet.name + +@description('The resource group the disk encryption set was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the disk encryption set.') +output systemAssignedPrincipalId string = diskEncryptionSet.identity.principalId + +@description('The name of the key vault with the disk encryption key.') +output keyVaultName string = last(split(keyVaultResourceId, '/')) + +@description('The location the resource was deployed into.') +output location string = diskEncryptionSet.location diff --git a/modules/Microsoft.Compute/diskEncryptionSets/readme.md b/modules/Microsoft.Compute/diskEncryptionSets/readme.md new file mode 100644 index 0000000..09934b2 --- /dev/null +++ b/modules/Microsoft.Compute/diskEncryptionSets/readme.md @@ -0,0 +1,233 @@ +# Disk Encryption Sets `[Microsoft.Compute/diskEncryptionSets]` + +This template deploys a disk encryption set. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/diskEncryptionSets` | [2021-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-04-01/diskEncryptionSets) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2021-06-01-preview/vaults/accessPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyName` | string | Key URL (with version) pointing to a key or secret in KeyVault. | +| `keyVaultResourceId` | string | Resource ID of the KeyVault containing the key or secret. | +| `name` | string | The name of the disk encryption set that is being created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `encryptionType` | string | `'EncryptionAtRestWithPlatformAndCustomerKeys'` | `[EncryptionAtRestWithCustomerKey, EncryptionAtRestWithPlatformAndCustomerKeys]` | The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys. | +| `keyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `location` | string | `[resourceGroup().location]` | | Resource location. | +| `roleAssignments` | array | `[]` | | 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'. | +| `rotationToLatestKeyVersionEnabled` | bool | `False` | | Set this flag to true to enable auto-updating of this disk encryption set to the latest key version. | +| `tags` | object | `{object}` | | Tags of the disk encryption resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the key vault with the disk encryption key. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the disk encryption set. | +| `resourceGroupName` | string | The resource group the disk encryption set was deployed into. | +| `resourceId` | string | The resource ID of the disk encryption set. | +| `systemAssignedPrincipalId` | string | The principal ID of the disk encryption set. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.KeyVault/vaults/accessPolicies` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module diskEncryptionSets './Microsoft.Compute/diskEncryptionSets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DiskEncryptionSets' + params: { + // Required parameters + keyName: 'keyEncryptionKey' + keyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + name: '<>-az-des-x-001' + // Non-required parameters + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "keyName": { + "value": "keyEncryptionKey" + }, + "keyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "name": { + "value": "<>-az-des-x-001" + }, + // Non-required parameters + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/diskEncryptionSets/version.json b/modules/Microsoft.Compute/diskEncryptionSets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/diskEncryptionSets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/disks/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/disks/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..c1364b9 --- /dev/null +++ b/modules/Microsoft.Compute/disks/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Disk Backup Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'Disk Restore Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13') + 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource disk 'Microsoft.Compute/disks@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(disk.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: disk +}] diff --git a/modules/Microsoft.Compute/disks/.test/image.parameters.json b/modules/Microsoft.Compute/disks/.test/image.parameters.json new file mode 100644 index 0000000..d6934ac --- /dev/null +++ b/modules/Microsoft.Compute/disks/.test/image.parameters.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-disk-image-001" + }, + "sku": { + "value": "Standard_LRS" + }, + "createOption": { + "value": "FromImage" + }, + "imageReferenceId": { + "value": "/Subscriptions/<>/Providers/Microsoft.Compute/Locations/westeurope/Publishers/MicrosoftWindowsServer/ArtifactTypes/VMImage/Offers/WindowsServer/Skus/2016-Datacenter/Versions/14393.4906.2112080838" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/disks/.test/import.parameters.json b/modules/Microsoft.Compute/disks/.test/import.parameters.json new file mode 100644 index 0000000..808508a --- /dev/null +++ b/modules/Microsoft.Compute/disks/.test/import.parameters.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-disk-import-001" + }, + "sku": { + "value": "Standard_LRS" + }, + "createOption": { + "value": "Import" + }, + "sourceUri": { + "value": "https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsavhd001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/disks/.test/min.parameters.json b/modules/Microsoft.Compute/disks/.test/min.parameters.json new file mode 100644 index 0000000..d19f33a --- /dev/null +++ b/modules/Microsoft.Compute/disks/.test/min.parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-disk-min-001" + }, + "sku": { + "value": "Standard_LRS" + }, + "diskSizeGB": { + "value": 1 + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/disks/.test/parameters.json b/modules/Microsoft.Compute/disks/.test/parameters.json new file mode 100644 index 0000000..833336e --- /dev/null +++ b/modules/Microsoft.Compute/disks/.test/parameters.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-disk-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "UltraSSD_LRS" + }, + "diskSizeGB": { + "value": 128 + }, + "logicalSectorSize": { + "value": 512 + }, + "diskIOPSReadWrite": { + "value": 500 + }, + "diskMBpsReadWrite": { + "value": 60 + }, + "osType": { + "value": "Windows" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/disks/deploy.bicep b/modules/Microsoft.Compute/disks/deploy.bicep new file mode 100644 index 0000000..b6d2a42 --- /dev/null +++ b/modules/Microsoft.Compute/disks/deploy.bicep @@ -0,0 +1,202 @@ +@description('Required. The name of the disk that is being created.') +param name string + +@description('Optional. Resource location.') +param location string = resourceGroup().location + +@allowed([ + 'Standard_LRS' + 'Premium_LRS' + 'StandardSSD_LRS' + 'UltraSSD_LRS' + 'Premium_ZRS' + 'Premium_ZRS' +]) +@description('Required. The disks sku name. Can be .') +param sku string + +@description('Optional. Set to true to enable bursting beyond the provisioned performance target of the disk.') +param burstingEnabled bool = false + +@description('Optional. Percentage complete for the background copy when a resource is created via the CopyStart operation.') +param completionPercent int = 100 + +@allowed([ + 'Attach' + 'Copy' + 'CopyStart' + 'Empty' + 'FromImage' + 'Import' + 'ImportSecure' + 'Restore' + 'Upload' + 'UploadPreparedSecure' +]) +@description('Optional. Sources of a disk creation.') +param createOption string = 'Empty' + +@description('Optional. A relative uri containing either a Platform Image Repository or user image reference.') +param imageReferenceId string = '' + +@description('Optional. Logical sector size in bytes for Ultra disks. Supported values are 512 ad 4096.') +param logicalSectorSize int = 4096 + +@description('Optional. If create option is ImportSecure, this is the URI of a blob to be imported into VM guest state.') +param securityDataUri string = '' + +@description('Optional. If create option is Copy, this is the ARM ID of the source snapshot or disk.') +param sourceResourceId string = '' + +@description('Optional. If create option is Import, this is the URI of a blob to be imported into a managed disk.') +param sourceUri string = '' + +@description('Optional. Required if create option is Import. The Azure Resource Manager identifier of the storage account containing the blob to import as a disk.') +param storageAccountId string = '' + +@description('Optional. If create option is Upload, this is the size of the contents of the upload including the VHD footer.') +param uploadSizeBytes int = 20972032 + +@description('Optional. If create option is empty, this field is mandatory and it indicates the size of the disk to create.') +param diskSizeGB int = 0 + +@description('Optional. The number of IOPS allowed for this disk; only settable for UltraSSD disks.') +param diskIOPSReadWrite int = 0 + +@description('Optional. The bandwidth allowed for this disk; only settable for UltraSSD disks.') +param diskMBpsReadWrite int = 0 + +@allowed([ + 'V1' + 'V2' +]) +@description('Optional. The hypervisor generation of the Virtual Machine. Applicable to OS disks only.') +param hyperVGeneration string = 'V2' + +@description('Optional. The maximum number of VMs that can attach to the disk at the same time. Default value is 0.') +param maxShares int = 1 + +@allowed([ + 'AllowAll' + 'AllowPrivate' + 'DenyAll' +]) +@description('Optional. Policy for accessing the disk via network.') +param networkAccessPolicy string = 'DenyAll' + +@allowed([ + 'Windows' + 'Linux' + '' +]) +@description('Optional. Sources of a disk creation.') +param osType string = '' + +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. Policy for controlling export on the disk.') +param publicNetworkAccess string = 'Disabled' + +@description('Optional. True if the image from which the OS disk is created supports accelerated networking.') +param acceleratedNetwork bool = false + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the availability set resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource disk 'Microsoft.Compute/disks@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: sku + } + properties: { + burstingEnabled: burstingEnabled + completionPercent: completionPercent + creationData: { + createOption: createOption + imageReference: createOption != 'FromImage' ? null : { + id: imageReferenceId + } + logicalSectorSize: contains(sku, 'Ultra') ? logicalSectorSize : null + securityDataUri: createOption == 'ImportSecure' ? securityDataUri : null + sourceResourceId: createOption == 'Copy' ? sourceResourceId : null + sourceUri: createOption == 'Import' ? sourceUri : null + storageAccountId: createOption == 'Import' ? storageAccountId : null + uploadSizeBytes: createOption == 'Upload' ? uploadSizeBytes : null + } + diskIOPSReadWrite: contains(sku, 'Ultra') ? diskIOPSReadWrite : null + diskMBpsReadWrite: contains(sku, 'Ultra') ? diskMBpsReadWrite : null + diskSizeGB: createOption == 'Empty' ? diskSizeGB : null + hyperVGeneration: empty(osType) ? null : hyperVGeneration + maxShares: maxShares + networkAccessPolicy: networkAccessPolicy + osType: empty(osType) ? any(null) : osType + publicNetworkAccess: publicNetworkAccess + supportedCapabilities: empty(osType) ? {} : { + acceleratedNetwork: acceleratedNetwork + } + } +} + +resource disk_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${disk.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: disk +} + +module disk_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AvSet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: disk.id + } +}] + +@description('The resource group the disk was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the disk.') +output resourceId string = disk.id + +@description('The name of the disk.') +output name string = disk.name + +@description('The location the resource was deployed into.') +output location string = disk.location diff --git a/modules/Microsoft.Compute/disks/readme.md b/modules/Microsoft.Compute/disks/readme.md new file mode 100644 index 0000000..d5c3698 --- /dev/null +++ b/modules/Microsoft.Compute/disks/readme.md @@ -0,0 +1,480 @@ +# Compute Disks `[Microsoft.Compute/disks]` + +This template deploys a disk + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/disks` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-08-01/disks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | The name of the disk that is being created. | +| `sku` | string | `[Premium_LRS, Premium_ZRS, Premium_ZRS, Standard_LRS, StandardSSD_LRS, UltraSSD_LRS]` | The disks sku name. Can be . | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `acceleratedNetwork` | bool | `False` | | True if the image from which the OS disk is created supports accelerated networking. | +| `burstingEnabled` | bool | `False` | | Set to true to enable bursting beyond the provisioned performance target of the disk. | +| `completionPercent` | int | `100` | | Percentage complete for the background copy when a resource is created via the CopyStart operation. | +| `createOption` | string | `'Empty'` | `[Attach, Copy, CopyStart, Empty, FromImage, Import, ImportSecure, Restore, Upload, UploadPreparedSecure]` | Sources of a disk creation. | +| `diskIOPSReadWrite` | int | `0` | | The number of IOPS allowed for this disk; only settable for UltraSSD disks. | +| `diskMBpsReadWrite` | int | `0` | | The bandwidth allowed for this disk; only settable for UltraSSD disks. | +| `diskSizeGB` | int | `0` | | If create option is empty, this field is mandatory and it indicates the size of the disk to create. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `hyperVGeneration` | string | `'V2'` | `[V1, V2]` | The hypervisor generation of the Virtual Machine. Applicable to OS disks only. | +| `imageReferenceId` | string | `''` | | A relative uri containing either a Platform Image Repository or user image reference. | +| `location` | string | `[resourceGroup().location]` | | Resource location. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `logicalSectorSize` | int | `4096` | | Logical sector size in bytes for Ultra disks. Supported values are 512 ad 4096. | +| `maxShares` | int | `1` | | The maximum number of VMs that can attach to the disk at the same time. Default value is 0. | +| `networkAccessPolicy` | string | `'DenyAll'` | `[AllowAll, AllowPrivate, DenyAll]` | Policy for accessing the disk via network. | +| `osType` | string | `''` | `['', Linux, Windows]` | Sources of a disk creation. | +| `publicNetworkAccess` | string | `'Disabled'` | `[Disabled, Enabled]` | Policy for controlling export on the disk. | +| `roleAssignments` | array | `[]` | | 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'. | +| `securityDataUri` | string | `''` | | If create option is ImportSecure, this is the URI of a blob to be imported into VM guest state. | +| `sourceResourceId` | string | `''` | | If create option is Copy, this is the ARM ID of the source snapshot or disk. | +| `sourceUri` | string | `''` | | If create option is Import, this is the URI of a blob to be imported into a managed disk. | +| `storageAccountId` | string | `''` | | Required if create option is Import. The Azure Resource Manager identifier of the storage account containing the blob to import as a disk. | +| `tags` | object | `{object}` | | Tags of the availability set resource. | +| `uploadSizeBytes` | int | `20972032` | | If create option is Upload, this is the size of the contents of the upload including the VHD footer. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the disk. | +| `resourceGroupName` | string | The resource group the disk was deployed into. | +| `resourceId` | string | The resource ID of the disk. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Image

+ +
+ +via Bicep module + +```bicep +module disks './Microsoft.Compute/disks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Disks' + params: { + // Required parameters + name: '<>-az-disk-image-001' + sku: 'Standard_LRS' + // Non-required parameters + createOption: 'FromImage' + imageReferenceId: '/Subscriptions/<>/Providers/Microsoft.Compute/Locations/westeurope/Publishers/MicrosoftWindowsServer/ArtifactTypes/VMImage/Offers/WindowsServer/Skus/2016-Datacenter/Versions/14393.4906.2112080838' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-disk-image-001" + }, + "sku": { + "value": "Standard_LRS" + }, + // Non-required parameters + "createOption": { + "value": "FromImage" + }, + "imageReferenceId": { + "value": "/Subscriptions/<>/Providers/Microsoft.Compute/Locations/westeurope/Publishers/MicrosoftWindowsServer/ArtifactTypes/VMImage/Offers/WindowsServer/Skus/2016-Datacenter/Versions/14393.4906.2112080838" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Import

+ +
+ +via Bicep module + +```bicep +module disks './Microsoft.Compute/disks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Disks' + params: { + // Required parameters + name: '<>-az-disk-import-001' + sku: 'Standard_LRS' + // Non-required parameters + createOption: 'Import' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sourceUri: 'https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd' + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsavhd001' + } +} +``` + +
+

+ +

+ +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": "<>-az-disk-import-001" + }, + "sku": { + "value": "Standard_LRS" + }, + // Non-required parameters + "createOption": { + "value": "Import" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sourceUri": { + "value": "https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsavhd001" + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module disks './Microsoft.Compute/disks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Disks' + params: { + // Required parameters + name: '<>-az-disk-min-001' + sku: 'Standard_LRS' + // Non-required parameters + diskSizeGB: 1 + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-disk-min-001" + }, + "sku": { + "value": "Standard_LRS" + }, + // Non-required parameters + "diskSizeGB": { + "value": 1 + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 4: Parameters

+ +
+ +via Bicep module + +```bicep +module disks './Microsoft.Compute/disks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Disks' + params: { + // Required parameters + name: '<>-az-disk-x-001' + sku: 'UltraSSD_LRS' + // Non-required parameters + diskIOPSReadWrite: 500 + diskMBpsReadWrite: 60 + diskSizeGB: 128 + lock: 'CanNotDelete' + logicalSectorSize: 512 + osType: 'Windows' + publicNetworkAccess: 'Enabled' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-disk-x-001" + }, + "sku": { + "value": "UltraSSD_LRS" + }, + // Non-required parameters + "diskIOPSReadWrite": { + "value": 500 + }, + "diskMBpsReadWrite": { + "value": 60 + }, + "diskSizeGB": { + "value": 128 + }, + "lock": { + "value": "CanNotDelete" + }, + "logicalSectorSize": { + "value": 512 + }, + "osType": { + "value": "Windows" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/disks/version.json b/modules/Microsoft.Compute/disks/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/disks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/galleries/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/galleries/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a79ea7f --- /dev/null +++ b/modules/Microsoft.Compute/galleries/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + '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 gallery 'Microsoft.Compute/galleries@2021-10-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(gallery.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: gallery +}] diff --git a/modules/Microsoft.Compute/galleries/.test/images.parameters.json b/modules/Microsoft.Compute/galleries/.test/images.parameters.json new file mode 100644 index 0000000..78a802c --- /dev/null +++ b/modules/Microsoft.Compute/galleries/.test/images.parameters.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsigweuimages001" + }, + "images": { + "value": [ + { + "name": "<>-az-imgd-x-003" + }, + { + "name": "<>-az-imgd-x-001", + "osType": "Windows", + "osState": "Generalized", + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2022-datacenter-azure-edition", + "minRecommendedvCPUs": 2, + "maxRecommendedvCPUs": 8, + "minRecommendedMemory": 4, + "maxRecommendedMemory": 16, + "hyperVGeneration": "V1", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "<>-az-imgd-x-002", + "osType": "Linux", + "osState": "Generalized", + "publisher": "canonical", + "offer": "0001-com-ubuntu-server-focal", + "sku": "20_04-lts-gen2", + "minRecommendedvCPUs": 1, + "maxRecommendedvCPUs": 4, + "minRecommendedMemory": 4, + "maxRecommendedMemory": 32, + "hyperVGeneration": "V2" + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/galleries/.test/parameters.json b/modules/Microsoft.Compute/galleries/.test/parameters.json new file mode 100644 index 0000000..960e036 --- /dev/null +++ b/modules/Microsoft.Compute/galleries/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsigweux001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/galleries/deploy.bicep b/modules/Microsoft.Compute/galleries/deploy.bicep new file mode 100644 index 0000000..85eafb6 --- /dev/null +++ b/modules/Microsoft.Compute/galleries/deploy.bicep @@ -0,0 +1,118 @@ +@minLength(1) +@description('Required. Name of the Azure Shared Image Gallery.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Description of the Azure Shared Image Gallery.') +param galleryDescription string = '' + +@description('Optional. Images to create.') +param images array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags for all resources.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource gallery 'Microsoft.Compute/galleries@2021-10-01' = { + name: name + location: location + tags: tags + properties: { + description: galleryDescription + identifier: {} + } +} + +resource gallery_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${gallery.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: gallery +} + +module gallery_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Gallery-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: gallery.id + } +}] + +// Images +module galleries_images 'images/deploy.bicep' = [for (image, index) in images: { + name: '${uniqueString(deployment().name, location)}-Gallery-Image-${index}' + params: { + name: image.name + galleryName: gallery.name + osType: contains(image, 'osType') ? image.osType : 'Windows' + osState: contains(image, 'osState') ? image.osState : 'Generalized' + publisher: contains(image, 'publisher') ? image.publisher : 'MicrosoftWindowsServer' + offer: contains(image, 'offer') ? image.offer : 'WindowsServer' + sku: contains(image, 'sku') ? image.sku : '2019-Datacenter' + minRecommendedvCPUs: contains(image, 'minRecommendedvCPUs') ? image.minRecommendedvCPUs : 1 + maxRecommendedvCPUs: contains(image, 'maxRecommendedvCPUs') ? image.maxRecommendedvCPUs : 4 + minRecommendedMemory: contains(image, 'minRecommendedMemory') ? image.minRecommendedMemory : 4 + maxRecommendedMemory: contains(image, 'maxRecommendedMemory') ? image.maxRecommendedMemory : 16 + hyperVGeneration: contains(image, 'hyperVGeneration') ? image.hyperVGeneration : 'V1' + imageDefinitionDescription: contains(image, 'imageDefinitionDescription') ? image.imageDefinitionDescription : '' + eula: contains(image, 'eula') ? image.eula : '' + privacyStatementUri: contains(image, 'privacyStatementUri') ? image.privacyStatementUri : '' + releaseNoteUri: contains(image, 'releaseNoteUri') ? image.releaseNoteUri : '' + productName: contains(image, 'productName') ? image.productName : '' + planName: contains(image, 'planName') ? image.planName : '' + planPublisherName: contains(image, 'planPublisherName') ? image.planPublisherName : '' + endOfLife: contains(image, 'endOfLife') ? image.endOfLife : '' + excludedDiskTypes: contains(image, 'excludedDiskTypes') ? image.excludedDiskTypes : [] + roleAssignments: contains(image, 'roleAssignments') ? image.roleAssignments : [] + tags: contains(image, 'tags') ? image.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The resource ID of the deployed image gallery.') +output resourceId string = gallery.id + +@description('The resource group of the deployed image gallery.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed image gallery.') +output name string = gallery.name + +@description('The location the resource was deployed into.') +output location string = gallery.location diff --git a/modules/Microsoft.Compute/galleries/images/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/galleries/images/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ca33748 --- /dev/null +++ b/modules/Microsoft.Compute/galleries/images/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'myCustomRoleAtSub': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60cb79d9-783a-50a4-9f05-d4c579fb8ce3') +} + +resource galleryImage 'Microsoft.Compute/galleries/images@2021-10-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(galleryImage.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: galleryImage +}] diff --git a/modules/Microsoft.Compute/galleries/images/deploy.bicep b/modules/Microsoft.Compute/galleries/images/deploy.bicep new file mode 100644 index 0000000..6df440b --- /dev/null +++ b/modules/Microsoft.Compute/galleries/images/deploy.bicep @@ -0,0 +1,176 @@ +@description('Required. Name of the image definition.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Conditional. The name of the parent Azure Shared Image Gallery. Required if the template is used in a standalone deployment.') +@minLength(1) +param galleryName string + +@description('Optional. OS type of the image to be created.') +@allowed([ + 'Windows' + 'Linux' +]) +param osType string = 'Windows' + +@description('Optional. This property allows the user to specify whether the virtual machines created under this image are \'Generalized\' or \'Specialized\'.') +@allowed([ + 'Generalized' + 'Specialized' +]) +param osState string = 'Generalized' + +@description('Optional. The name of the gallery Image Definition publisher.') +param publisher string = 'MicrosoftWindowsServer' + +@description('Optional. The name of the gallery Image Definition offer.') +param offer string = 'WindowsServer' + +@description('Optional. The name of the gallery Image Definition SKU.') +param sku string = '2019-Datacenter' + +@description('Optional. The minimum number of the CPU cores recommended for this image.') +@minValue(1) +@maxValue(128) +param minRecommendedvCPUs int = 1 + +@description('Optional. The maximum number of the CPU cores recommended for this image.') +@minValue(1) +@maxValue(128) +param maxRecommendedvCPUs int = 4 + +@description('Optional. The minimum amount of RAM in GB recommended for this image.') +@minValue(1) +@maxValue(4000) +param minRecommendedMemory int = 4 + +@description('Optional. The maximum amount of RAM in GB recommended for this image.') +@minValue(1) +@maxValue(4000) +param maxRecommendedMemory int = 16 + +@description('Optional. The hypervisor generation of the Virtual Machine. Applicable to OS disks only. - V1 or V2.') +@allowed([ + 'V1' + 'V2' +]) +param hyperVGeneration string = 'V1' + +@description('Optional. The description of this gallery Image Definition resource. This property is updatable.') +param imageDefinitionDescription string = '' + +@description('Optional. The Eula agreement for the gallery Image Definition. Has to be a valid URL.') +param eula string = '' + +@description('Optional. The privacy statement uri. Has to be a valid URL.') +param privacyStatementUri string = '' + +@description('Optional. The release note uri. Has to be a valid URL.') +param releaseNoteUri string = '' + +@description('Optional. The product ID.') +param productName string = '' + +@description('Optional. The plan ID.') +param planName string = '' + +@description('Optional. The publisher ID.') +param planPublisherName string = '' + +@description('Optional. The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z.') +param endOfLife string = '' + +@description('Optional. List of the excluded disk types. E.g. Standard_LRS.') +param excludedDiskTypes array = [] + +@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. Tags for all resources.') +param tags object = {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource gallery 'Microsoft.Compute/galleries@2021-10-01' existing = { + name: galleryName +} + +resource image 'Microsoft.Compute/galleries/images@2021-10-01' = { + name: name + parent: gallery + location: location + tags: tags + properties: { + osType: osType + osState: osState + identifier: { + publisher: publisher + offer: offer + sku: sku + } + recommended: { + vCPUs: { + min: minRecommendedvCPUs + max: maxRecommendedvCPUs + } + memory: { + min: minRecommendedMemory + max: maxRecommendedMemory + } + } + hyperVGeneration: hyperVGeneration + description: imageDefinitionDescription + eula: eula + privacyStatementUri: privacyStatementUri + releaseNoteUri: releaseNoteUri + purchasePlan: { + product: !empty(productName) ? productName : null + name: !empty(planName) ? planName : null + publisher: !empty(planPublisherName) ? planPublisherName : null + } + endOfLifeDate: endOfLife + disallowed: { + diskTypes: excludedDiskTypes + } + } +} + +module galleryImage_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: image.id + } +}] + +@description('The resource group the image was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the image.') +output resourceId string = image.id + +@description('The name of the image.') +output name string = image.name + +@description('The location the resource was deployed into.') +output location string = image.location diff --git a/modules/Microsoft.Compute/galleries/images/readme.md b/modules/Microsoft.Compute/galleries/images/readme.md new file mode 100644 index 0000000..e380c9d --- /dev/null +++ b/modules/Microsoft.Compute/galleries/images/readme.md @@ -0,0 +1,170 @@ +# Shared Image Definition `[Microsoft.Compute/galleries/images]` + +This module deploys an Image Definition in a Shared Image Gallery. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/galleries/images` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-10-01/galleries/images) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the image definition. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `galleryName` | string | The name of the parent Azure Shared Image Gallery. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `endOfLife` | string | `''` | | The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z. | +| `eula` | string | `''` | | The Eula agreement for the gallery Image Definition. Has to be a valid URL. | +| `excludedDiskTypes` | array | `[]` | | List of the excluded disk types. E.g. Standard_LRS. | +| `hyperVGeneration` | string | `'V1'` | `[V1, V2]` | The hypervisor generation of the Virtual Machine. Applicable to OS disks only. - V1 or V2. | +| `imageDefinitionDescription` | string | `''` | | The description of this gallery Image Definition resource. This property is updatable. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `maxRecommendedMemory` | int | `16` | | The maximum amount of RAM in GB recommended for this image. | +| `maxRecommendedvCPUs` | int | `4` | | The maximum number of the CPU cores recommended for this image. | +| `minRecommendedMemory` | int | `4` | | The minimum amount of RAM in GB recommended for this image. | +| `minRecommendedvCPUs` | int | `1` | | The minimum number of the CPU cores recommended for this image. | +| `offer` | string | `'WindowsServer'` | | The name of the gallery Image Definition offer. | +| `osState` | string | `'Generalized'` | `[Generalized, Specialized]` | This property allows the user to specify whether the virtual machines created under this image are 'Generalized' or 'Specialized'. | +| `osType` | string | `'Windows'` | `[Linux, Windows]` | OS type of the image to be created. | +| `planName` | string | `''` | | The plan ID. | +| `planPublisherName` | string | `''` | | The publisher ID. | +| `privacyStatementUri` | string | `''` | | The privacy statement uri. Has to be a valid URL. | +| `productName` | string | `''` | | The product ID. | +| `publisher` | string | `'MicrosoftWindowsServer'` | | The name of the gallery Image Definition publisher. | +| `releaseNoteUri` | string | `''` | | The release note uri. Has to be a valid URL. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'2019-Datacenter'` | | The name of the gallery Image Definition SKU. | +| `tags` | object | `{object}` | | Tags for all resources. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the image. | +| `resourceGroupName` | string | The resource group the image was deployed into. | +| `resourceId` | string | The resource ID of the image. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Compute/galleries/images/version.json b/modules/Microsoft.Compute/galleries/images/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/galleries/images/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/galleries/readme.md b/modules/Microsoft.Compute/galleries/readme.md new file mode 100644 index 0000000..9aadc15 --- /dev/null +++ b/modules/Microsoft.Compute/galleries/readme.md @@ -0,0 +1,343 @@ +# Azure Compute Galleries `[Microsoft.Compute/galleries]` + +This module deploys an Azure compute gallery (formerly known as shared image gallery). + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/galleries` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-10-01/galleries) | +| `Microsoft.Compute/galleries/images` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-10-01/galleries/images) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Shared Image Gallery. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `galleryDescription` | string | `''` | | Description of the Azure Shared Image Gallery. | +| `images` | _[images](images/readme.md)_ array | `[]` | | Images to create. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags for all resources. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed image gallery. | +| `resourceGroupName` | string | The resource group of the deployed image gallery. | +| `resourceId` | string | The resource ID of the deployed image gallery. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Images

+ +
+ +via Bicep module + +```bicep +module galleries './Microsoft.Compute/galleries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Galleries' + params: { + // Required parameters + name: '<>azsigweuimages001' + // Non-required parameters + images: [ + { + name: '<>-az-imgd-x-003' + } + { + hyperVGeneration: 'V1' + maxRecommendedMemory: 16 + maxRecommendedvCPUs: 8 + minRecommendedMemory: 4 + minRecommendedvCPUs: 2 + name: '<>-az-imgd-x-001' + offer: 'WindowsServer' + osState: 'Generalized' + osType: 'Windows' + publisher: 'MicrosoftWindowsServer' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sku: '2022-datacenter-azure-edition' + } + { + hyperVGeneration: 'V2' + maxRecommendedMemory: 32 + maxRecommendedvCPUs: 4 + minRecommendedMemory: 4 + minRecommendedvCPUs: 1 + name: '<>-az-imgd-x-002' + offer: '0001-com-ubuntu-server-focal' + osState: 'Generalized' + osType: 'Linux' + publisher: 'canonical' + sku: '20_04-lts-gen2' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>azsigweuimages001" + }, + // Non-required parameters + "images": { + "value": [ + { + "name": "<>-az-imgd-x-003" + }, + { + "hyperVGeneration": "V1", + "maxRecommendedMemory": 16, + "maxRecommendedvCPUs": 8, + "minRecommendedMemory": 4, + "minRecommendedvCPUs": 2, + "name": "<>-az-imgd-x-001", + "offer": "WindowsServer", + "osState": "Generalized", + "osType": "Windows", + "publisher": "MicrosoftWindowsServer", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "sku": "2022-datacenter-azure-edition" + }, + { + "hyperVGeneration": "V2", + "maxRecommendedMemory": 32, + "maxRecommendedvCPUs": 4, + "minRecommendedMemory": 4, + "minRecommendedvCPUs": 1, + "name": "<>-az-imgd-x-002", + "offer": "0001-com-ubuntu-server-focal", + "osState": "Generalized", + "osType": "Linux", + "publisher": "canonical", + "sku": "20_04-lts-gen2" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module galleries './Microsoft.Compute/galleries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Galleries' + params: { + // Required parameters + name: '<>azsigweux001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>azsigweux001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/galleries/version.json b/modules/Microsoft.Compute/galleries/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/galleries/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/images/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/images/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..f143080 --- /dev/null +++ b/modules/Microsoft.Compute/images/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'myCustomRoleAtSub': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60cb79d9-783a-50a4-9f05-d4c579fb8ce3') +} + +resource image 'Microsoft.Compute/images@2021-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(image.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: image +}] diff --git a/modules/Microsoft.Compute/images/.test/parameters.json b/modules/Microsoft.Compute/images/.test/parameters.json new file mode 100644 index 0000000..fed4676 --- /dev/null +++ b/modules/Microsoft.Compute/images/.test/parameters.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-img-x-001" + }, + "osAccountType": { + "value": "Premium_LRS" + }, + "osType": { + "value": "Windows" + }, + "osDiskBlobUri": { + "value": "https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd" + }, + "osDiskCaching": { + "value": "ReadWrite" + }, + "zoneResilient": { + "value": true + }, + "hyperVGeneration": { + "value": "V1" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/images/deploy.bicep b/modules/Microsoft.Compute/images/deploy.bicep new file mode 100644 index 0000000..a4a27a0 --- /dev/null +++ b/modules/Microsoft.Compute/images/deploy.bicep @@ -0,0 +1,88 @@ +@description('Required. The name of the image.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The Virtual Hard Disk.') +param osDiskBlobUri string + +@description('Required. This property allows you to specify the type of the OS that is included in the disk if creating a VM from a custom image. - Windows or Linux.') +param osType string + +@description('Optional. Specifies the caching requirements. Default: None for Standard storage. ReadOnly for Premium storage. - None, ReadOnly, ReadWrite.') +param osDiskCaching string + +@description('Optional. Specifies the storage account type for the managed disk. NOTE: UltraSSD_LRS can only be used with data disks, it cannot be used with OS Disk. - Standard_LRS, Premium_LRS, StandardSSD_LRS, UltraSSD_LRS.') +param osAccountType string + +@description('Optional. Default is false. Specifies whether an image is zone resilient or not. Zone resilient images can be created only in regions that provide Zone Redundant Storage (ZRS).') +param zoneResilient bool = false + +@description('Optional. Gets the HyperVGenerationType of the VirtualMachine created from the image. - V1 or V2.') +param hyperVGeneration string = 'V1' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource image 'Microsoft.Compute/images@2021-04-01' = { + name: name + location: location + tags: tags + properties: { + storageProfile: { + osDisk: { + osType: osType + blobUri: osDiskBlobUri + caching: osDiskCaching + storageAccountType: osAccountType + } + dataDisks: [] + zoneResilient: zoneResilient + } + hyperVGeneration: hyperVGeneration + } +} + +module image_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Image-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: image.id + } +}] + +@description('The resource ID of the image.') +output resourceId string = image.id + +@description('The resource group the image was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the image.') +output name string = image.name + +@description('The location the resource was deployed into.') +output location string = image.location diff --git a/modules/Microsoft.Compute/images/readme.md b/modules/Microsoft.Compute/images/readme.md new file mode 100644 index 0000000..c2af93b --- /dev/null +++ b/modules/Microsoft.Compute/images/readme.md @@ -0,0 +1,243 @@ +# Images `[Microsoft.Compute/images]` + +This module deploys a compute image. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/images` | [2021-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-04-01/images) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the image. | +| `osDiskBlobUri` | string | The Virtual Hard Disk. | +| `osType` | string | This property allows you to specify the type of the OS that is included in the disk if creating a VM from a custom image. - Windows or Linux. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `hyperVGeneration` | string | `'V1'` | Gets the HyperVGenerationType of the VirtualMachine created from the image. - V1 or V2. | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `osAccountType` | string | | Specifies the storage account type for the managed disk. NOTE: UltraSSD_LRS can only be used with data disks, it cannot be used with OS Disk. - Standard_LRS, Premium_LRS, StandardSSD_LRS, UltraSSD_LRS. | +| `osDiskCaching` | string | | Specifies the caching requirements. Default: None for Standard storage. ReadOnly for Premium storage. - None, ReadOnly, ReadWrite. | +| `roleAssignments` | array | `[]` | 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}` | Tags of the resource. | +| `zoneResilient` | bool | `False` | Default is false. Specifies whether an image is zone resilient or not. Zone resilient images can be created only in regions that provide Zone Redundant Storage (ZRS). | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the image. | +| `resourceGroupName` | string | The resource group the image was deployed into. | +| `resourceId` | string | The resource ID of the image. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module images './Microsoft.Compute/images/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Images' + params: { + // Required parameters + name: '<>-az-img-x-001' + osAccountType: 'Premium_LRS' + osDiskBlobUri: 'https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd' + osDiskCaching: 'ReadWrite' + osType: 'Windows' + // Non-required parameters + hyperVGeneration: 'V1' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + zoneResilient: true + } +} +``` + +
+

+ +

+ +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": "<>-az-img-x-001" + }, + "osAccountType": { + "value": "Premium_LRS" + }, + "osDiskBlobUri": { + "value": "https://adp<>azsavhd001.blob.core.windows.net/vhds/adp-<>-az-imgt-vhd-001.vhd" + }, + "osDiskCaching": { + "value": "ReadWrite" + }, + "osType": { + "value": "Windows" + }, + // Non-required parameters + "hyperVGeneration": { + "value": "V1" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "zoneResilient": { + "value": true + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/images/version.json b/modules/Microsoft.Compute/images/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/images/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/proximityPlacementGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/proximityPlacementGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..f0b623e --- /dev/null +++ b/modules/Microsoft.Compute/proximityPlacementGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,74 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + '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 proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@2021-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(proximityPlacementGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: proximityPlacementGroup +}] diff --git a/modules/Microsoft.Compute/proximityPlacementGroups/.test/parameters.json b/modules/Microsoft.Compute/proximityPlacementGroups/.test/parameters.json new file mode 100644 index 0000000..48ab4ed --- /dev/null +++ b/modules/Microsoft.Compute/proximityPlacementGroups/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ppg-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/proximityPlacementGroups/deploy.bicep b/modules/Microsoft.Compute/proximityPlacementGroups/deploy.bicep new file mode 100644 index 0000000..2e07087 --- /dev/null +++ b/modules/Microsoft.Compute/proximityPlacementGroups/deploy.bicep @@ -0,0 +1,84 @@ +@description('Required. The name of the proximity placement group that is being created.') +param name string + +@description('Optional. Specifies the type of the proximity placement group.') +@allowed([ + 'Standard' + 'Ultra' +]) +param proximityPlacementGroupType string = 'Standard' + +@description('Optional. Resource location.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the proximity placement group resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@2021-04-01' = { + name: name + location: location + tags: tags + properties: { + proximityPlacementGroupType: proximityPlacementGroupType + } +} + +resource proximityPlacementGroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${proximityPlacementGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: proximityPlacementGroup +} + +module proximityPlacementGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ProxPlaceGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: proximityPlacementGroup.id + } +}] + +@description('The name of the proximity placement group.') +output name string = proximityPlacementGroup.name + +@description('The resourceId the proximity placement group.') +output resourceId string = proximityPlacementGroup.id + +@description('The resource group the proximity placement group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = proximityPlacementGroup.location diff --git a/modules/Microsoft.Compute/proximityPlacementGroups/readme.md b/modules/Microsoft.Compute/proximityPlacementGroups/readme.md new file mode 100644 index 0000000..32d429e --- /dev/null +++ b/modules/Microsoft.Compute/proximityPlacementGroups/readme.md @@ -0,0 +1,220 @@ +# Proximity Placement Groups `[Microsoft.Compute/proximityPlacementGroups]` + +This template deploys a proximity placement group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/proximityPlacementGroups` | [2021-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-04-01/proximityPlacementGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the proximity placement group that is being created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Resource location. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `proximityPlacementGroupType` | string | `'Standard'` | `[Standard, Ultra]` | Specifies the type of the proximity placement group. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the proximity placement group resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the proximity placement group. | +| `resourceGroupName` | string | The resource group the proximity placement group was deployed into. | +| `resourceId` | string | The resourceId the proximity placement group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module proximityPlacementGroups './Microsoft.Compute/proximityPlacementGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ProximityPlacementGroups' + params: { + // Required parameters + name: '<>-az-ppg-x-001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-ppg-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/proximityPlacementGroups/version.json b/modules/Microsoft.Compute/proximityPlacementGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/proximityPlacementGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/virtualMachineScaleSets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..3da82a5 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2021-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(vmss.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: vmss +}] diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.min.parameters.json b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.min.parameters.json new file mode 100644 index 0000000..a160f36 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.min.parameters.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-scaleset-linux-min-001" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "skuName": { + "value": "Standard_B12ms" + }, + "imageReference": { + "value": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "adminUsername": { + "value": "scaleSetAdmin" + }, + "disablePasswordAuthentication": { + "value": true + }, + "publicKeys": { + "value": [ + { + "path": "/home/scaleSetAdmin/.ssh/authorized_keys", + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure" + } + ] + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic01", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.parameters.json b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.parameters.json new file mode 100644 index 0000000..ca6b1b3 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/linux.parameters.json @@ -0,0 +1,189 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-scaleset-linux-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "vmNamePrefix": { + "value": "vmsslinvm" + }, + "skuName": { + "value": "Standard_B12ms" + }, + "skuCapacity": { + "value": 1 + }, + "upgradePolicyMode": { + "value": "Manual" + }, + "vmPriority": { + "value": "Regular" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "availabilityZones": { + "value": [ + "2" + ] + }, + "scaleSetFaultDomain": { + "value": 1 + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "bootDiagnosticStorageAccountName": { + "value": "adp<>azsax001" + }, + "osType": { + "value": "Linux" + }, + "encryptionAtHost": { + "value": false + }, + "imageReference": { + "value": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "adminUsername": { + "value": "scaleSetAdmin" + }, + "disablePasswordAuthentication": { + "value": true + }, + "publicKeys": { + "value": [ + { + "path": "/home/scaleSetAdmin/.ssh/authorized_keys", + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure" + } + ] + }, + "dataDisks": { + "value": [ + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "256", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic01", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "VolumeType": "All", + "ResizeOSDisk": "false" + } + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1", + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ], + "protectedSettings": { + "commandToExecute": "sudo apt-get update" + } + } + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.min.parameters.json b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.min.parameters.json new file mode 100644 index 0000000..cb84878 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.min.parameters.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-scaleset-win-min-001" + }, + "skuName": { + "value": "Standard_B12ms" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } + }, + "adminUsername": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminUsername" + } + }, + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic01", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.parameters.json b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.parameters.json new file mode 100644 index 0000000..28cb36a --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/.test/windows.parameters.json @@ -0,0 +1,188 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-scaleset-win-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "vmNamePrefix": { + "value": "vmsswinvm" + }, + "skuName": { + "value": "Standard_B12ms" + }, + "skuCapacity": { + "value": 1 + }, + "upgradePolicyMode": { + "value": "Manual" + }, + "vmPriority": { + "value": "Regular" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "encryptionAtHost": { + "value": false + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } + }, + "adminUsername": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminUsername" + } + }, + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic01", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "proximityPlacementGroupResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vmss-001" + }, + "extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": true, + "Exclusions": { + "Extensions": ".log;.ldf", + "Paths": "D:\\IISlogs;D:\\DatabaseLogs", + "Processes": "mssence.svc" + }, + "RealtimeProtectionEnabled": true, + "ScheduledScanSettings": { + "isEnabled": "true", + "scanType": "Quick", + "day": "7", + "time": "120" + } + } + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "VolumeType": "All", + "ResizeOSDisk": "false" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": true + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1", + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ], + "protectedSettings": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"& .\\scriptExtensionMasterInstaller.ps1\"" + } + } + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/deploy.bicep b/modules/Microsoft.Compute/virtualMachineScaleSets/deploy.bicep new file mode 100644 index 0000000..54d14c7 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/deploy.bicep @@ -0,0 +1,662 @@ +@description('Required. Name of the VMSS.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your virtual machine scale sets.') +param encryptionAtHost bool = true + +@description('Optional. Specifies the SecurityType of the virtual machine scale set. It is set as TrustedLaunch to enable UefiSettings.') +param securityType string = '' + +@description('Optional. Specifies whether secure boot should be enabled on the virtual machine scale set. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings.') +param secureBootEnabled bool = false + +@description('Optional. Specifies whether vTPM should be enabled on the virtual machine scale set. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings.') +param vTpmEnabled bool = false + +@description('Required. OS image reference. In case of marketplace images, it\'s the combination of the publisher, offer, sku, version attributes. In case of custom images it\'s the resource ID of the custom image.') +param imageReference object + +@description('Optional. Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use.') +param plan object = {} + +@description('Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets.') +param osDisk object + +@description('Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets.') +param dataDisks array = [] + +@description('Optional. The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled.') +param ultraSSDEnabled bool = false + +@description('Required. Administrator username.') +@secure() +param adminUsername string + +@description('Optional. When specifying a Windows Virtual Machine, this value should be passed.') +@secure() +param adminPassword string = '' + +@description('Optional. Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format.') +param customData string = '' + +@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. Fault Domain count for each placement group.') +param scaleSetFaultDomain int = 2 + +@description('Optional. Resource ID of a proximity placement group.') +param proximityPlacementGroupResourceId string = '' + +@description('Required. Configures NICs and PIPs.') +param nicConfigurations array = [] + +@description('Optional. Specifies the priority for the virtual machine.') +@allowed([ + 'Regular' + 'Low' + 'Spot' +]) +param vmPriority string = 'Regular' + +@description('Optional. Specifies the eviction policy for the low priority virtual machine. Will result in \'Deallocate\' eviction policy.') +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 = '' + +@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([ + 'Windows_Client' + 'Windows_Server' + '' +]) +param licenseType string = '' + +@description('Optional. Specifies if Windows VM disks should be encrypted with Server-side encryption + Customer managed Key.') +param enableServerSideEncryption bool = false + +@description('Optional. Required if name is specified. Password of the user specified in user parameter.') +@secure() +param extensionDomainJoinPassword string = '' + +@description('Optional. The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDomainJoinConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Anti Malware] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionAntiMalwareConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionMonitoringAgentConfig object = { + enabled: false +} + +@description('Optional. Resource ID of the monitoring log analytics workspace.') +param monitoringWorkspaceId string = '' + +@description('Optional. The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDependencyAgentConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionNetworkWatcherAgentConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Disk Encryption] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDiskEncryptionConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDSCConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionCustomScriptConfig object = { + enabled: false + fileData: [] +} + +@description('Optional. Storage account boot diagnostic base URI.') +param bootDiagnosticStorageAccountUri string = '.blob.${environment().suffixes.storage}/' + +@description('Optional. Storage account used to store boot diagnostic information. Boot diagnostics will be disabled if no value is provided.') +param bootDiagnosticStorageAccountName string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Specifies the mode of an upgrade to virtual machines in the scale set.\' Manual - You control the application of updates to virtual machines in the scale set. You do this by using the manualUpgrade action. ; Automatic - All virtual machines in the scale set are automatically updated at the same time. - Automatic, Manual, Rolling.') +@allowed([ + 'Manual' + 'Automatic' + 'Rolling' +]) +param upgradePolicyMode string = 'Manual' + +@description('Optional. 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.') +param maxBatchInstancePercent int = 20 + +@description('Optional. 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.') +param maxUnhealthyInstancePercent int = 20 + +@description('Optional. 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.') +param maxUnhealthyUpgradedInstancePercent int = 20 + +@description('Optional. 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.') +param pauseTimeBetweenBatches string = 'PT0S' + +@description('Optional. Indicates whether OS upgrades should automatically be applied to scale set instances in a rolling fashion when a newer version of the OS image becomes available. Default value is false. If this is set to true for Windows based scale sets, enableAutomaticUpdates is automatically set to false and cannot be set to true.') +param enableAutomaticOSUpgrade bool = false + +@description('Optional. Whether OS image rollback feature should be disabled.') +param disableAutomaticRollback bool = false + +@description('Optional. Specifies whether automatic repairs should be enabled on the virtual machine scale set.') +param automaticRepairsPolicyEnabled bool = false + +@description('Optional. The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M).') +param gracePeriod string = 'PT30M' + +@description('Optional. Specifies the computer name prefix for all of the virtual machines in the scale set.') +@minLength(1) +@maxLength(15) +param vmNamePrefix string = 'vmssvm' + +@description('Optional. Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later.') +param provisionVMAgent bool = true + +@description('Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning.') +param enableAutomaticUpdates bool = true + +@description('Optional. Specifies the time zone of the virtual machine. e.g. \'Pacific Standard Time\'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`.') +param timeZone string = '' + +@description('Optional. Specifies additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. - AdditionalUnattendContent object.') +param additionalUnattendContent array = [] + +@description('Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object.') +param winRM object = {} + +@description('Optional. Specifies whether password authentication should be disabled.') +#disable-next-line secure-secrets-in-params // Not a secret +param disablePasswordAuthentication bool = false + +@description('Optional. The list of SSH public keys used to authenticate with linux based VMs.') +param publicKeys array = [] + +@description('Optional. Specifies set of certificates that should be installed onto the virtual machines in the scale set.') +#disable-next-line secure-secrets-in-params // Not a secret +param secrets array = [] + +@description('Optional. Specifies Scheduled Event related configurations.') +param scheduledEventsProfile object = {} + +@description('Optional. Specifies whether the Virtual Machine Scale Set should be overprovisioned.') +param overprovision bool = false + +@description('Optional. When Overprovision is enabled, extensions are launched only on the requested number of VMs which are finally kept. This property will hence ensure that the extensions do not run on the extra overprovisioned VMs.') +param doNotRunExtensionsOnOverprovisionedVMs bool = false + +@description('Optional. Whether to force strictly even Virtual Machine distribution cross x-zones in case there is zone outage.') +param zoneBalance bool = false + +@description('Optional. When true this limits the scale set to a single placement group, of max size 100 virtual machines. NOTE: If singlePlacementGroup is true, it may be modified to false. However, if singlePlacementGroup is false, it may not be modified to true.') +param singlePlacementGroup bool = true + +@description('Optional. Specifies the scale-in policy that decides which virtual machines are chosen for removal when a Virtual Machine Scale Set is scaled-in.') +param scaleInPolicy object = { + rules: [ + 'Default' + ] +} + +@description('Required. The SKU size of the VMs.') +param skuName string + +@description('Optional. The initial instance count of scale set VMs.') +param skuCapacity int = 1 + +@description('Optional. The virtual machine scale set zones. NOTE: Availability zones can only be set when you create the scale set.') +param availabilityZones array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. The chosen OS type.') +@allowed([ + 'Windows' + 'Linux' +]) +param osType string + +@description('Generated. Do not provide a value! This date value is used to generate a registration token.') +param baseTime string = utcNow('u') + +@description('Optional. SAS token validity length to use to download files from storage accounts. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours.') +param sasTokenValidityLength string = 'PT8H' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param publicIpDiagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var publicKeysFormatted = [for publicKey in publicKeys: { + path: publicKey.path + keyData: publicKey.keyData +}] + +var linuxConfiguration = { + disablePasswordAuthentication: disablePasswordAuthentication + ssh: { + publicKeys: publicKeysFormatted + } + provisionVMAgent: provisionVMAgent +} + +var windowsConfiguration = { + provisionVMAgent: provisionVMAgent + enableAutomaticUpdates: enableAutomaticUpdates + timeZone: empty(timeZone) ? null : timeZone + additionalUnattendContent: empty(additionalUnattendContent) ? null : additionalUnattendContent + winRM: !empty(winRM) ? { + listeners: winRM + } : null +} + +var accountSasProperties = { + signedServices: 'b' + signedPermission: 'r' + signedExpiry: dateTimeAdd(baseTime, sasTokenValidityLength) + signedResourceTypes: 'o' + signedProtocol: 'https' +} + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2021-04-01' = { + name: name + location: location + tags: tags + identity: identity + zones: availabilityZones + properties: { + proximityPlacementGroup: !empty(proximityPlacementGroupResourceId) ? { + id: proximityPlacementGroupResourceId + } : null + upgradePolicy: { + mode: upgradePolicyMode + rollingUpgradePolicy: { + maxBatchInstancePercent: maxBatchInstancePercent + maxUnhealthyInstancePercent: maxUnhealthyInstancePercent + maxUnhealthyUpgradedInstancePercent: maxUnhealthyUpgradedInstancePercent + pauseTimeBetweenBatches: pauseTimeBetweenBatches + } + automaticOSUpgradePolicy: { + enableAutomaticOSUpgrade: enableAutomaticOSUpgrade + disableAutomaticRollback: disableAutomaticRollback + } + } + automaticRepairsPolicy: { + enabled: automaticRepairsPolicyEnabled + gracePeriod: gracePeriod + } + virtualMachineProfile: { + osProfile: { + computerNamePrefix: vmNamePrefix + adminUsername: adminUsername + adminPassword: !empty(adminPassword) ? adminPassword : null + customData: !empty(customData) ? base64(customData) : null + windowsConfiguration: osType == 'Windows' ? windowsConfiguration : null + linuxConfiguration: osType == 'Linux' ? linuxConfiguration : null + secrets: secrets + } + securityProfile: { + encryptionAtHost: encryptionAtHost ? encryptionAtHost : null + securityType: securityType + uefiSettings: securityType == 'TrustedLaunch' ? { + secureBootEnabled: secureBootEnabled + vTpmEnabled: vTpmEnabled + } : null + } + storageProfile: { + imageReference: imageReference + osDisk: { + createOption: osDisk.createOption + diskSizeGB: osDisk.diskSizeGB + caching: contains(osDisk, 'caching') ? osDisk.caching : null + writeAcceleratorEnabled: contains(osDisk, 'writeAcceleratorEnabled') ? osDisk.writeAcceleratorEnabled : null + diffDiskSettings: contains(osDisk, 'diffDiskSettings') ? osDisk.diffDiskSettings : null + osType: contains(osDisk, 'osType') ? osDisk.osType : null + image: contains(osDisk, 'image') ? osDisk.image : null + vhdContainers: contains(osDisk, 'vhdContainers') ? osDisk.vhdContainers : null + managedDisk: { + storageAccountType: osDisk.managedDisk.storageAccountType + diskEncryptionSet: contains(osDisk.managedDisk, 'diskEncryptionSet') ? osDisk.managedDisk.diskEncryptionSet : null + } + } + dataDisks: [for (item, j) in dataDisks: { + lun: j + diskSizeGB: item.diskSizeGB + createOption: item.createOption + caching: item.caching + writeAcceleratorEnabled: contains(osDisk, 'writeAcceleratorEnabled') ? osDisk.writeAcceleratorEnabled : null + managedDisk: { + storageAccountType: item.managedDisk.storageAccountType + diskEncryptionSet: { + id: enableServerSideEncryption ? item.managedDisk.diskEncryptionSet.id : null + } + } + diskIOPSReadWrite: contains(osDisk, 'diskIOPSReadWrite') ? item.diskIOPSReadWrite : null + diskMBpsReadWrite: contains(osDisk, 'diskMBpsReadWrite') ? item.diskMBpsReadWrite : null + }] + } + networkProfile: { + networkInterfaceConfigurations: [for (nicConfiguration, index) in nicConfigurations: { + name: '${name}${nicConfiguration.nicSuffix}configuration-${index}' + properties: { + primary: (index == 0) ? true : any(null) + enableAcceleratedNetworking: contains(nicConfiguration, 'enableAcceleratedNetworking') ? nicConfiguration.enableAcceleratedNetworking : true + networkSecurityGroup: contains(nicConfiguration, 'nsgId') ? { + id: nicConfiguration.nsgId + } : null + ipConfigurations: nicConfiguration.ipConfigurations + } + }] + } + diagnosticsProfile: { + bootDiagnostics: { + enabled: !empty(bootDiagnosticStorageAccountName) + storageUri: !empty(bootDiagnosticStorageAccountName) ? 'https://${bootDiagnosticStorageAccountName}${bootDiagnosticStorageAccountUri}' : null + } + } + licenseType: empty(licenseType) ? null : licenseType + priority: vmPriority + evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null + billingProfile: !empty(vmPriority) && !empty(maxPriceForLowPriorityVm) ? json('{"maxPrice":"${maxPriceForLowPriorityVm}"}') : null + scheduledEventsProfile: scheduledEventsProfile + } + overprovision: overprovision + doNotRunExtensionsOnOverprovisionedVMs: doNotRunExtensionsOnOverprovisionedVMs + zoneBalance: zoneBalance == 'true' ? zoneBalance : null + platformFaultDomainCount: scaleSetFaultDomain + singlePlacementGroup: singlePlacementGroup + additionalCapabilities: { + ultraSSDEnabled: ultraSSDEnabled + } + scaleInPolicy: scaleInPolicy + } + sku: { + name: skuName + capacity: skuCapacity + } + plan: !empty(plan) ? plan : null +} + +module vmss_domainJoinExtension 'extensions/deploy.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 + } + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vmss_microsoftAntiMalwareExtension 'extensions/deploy.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource vmss_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(monitoringWorkspaceId)) { + name: last(split(monitoringWorkspaceId, '/')) + scope: resourceGroup(split(monitoringWorkspaceId, '/')[2], split(monitoringWorkspaceId, '/')[4]) +} + +module vmss_microsoftMonitoringAgentExtension 'extensions/deploy.bicep' = if (extensionMonitoringAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-MicrosoftMonitoringAgent' + params: { + virtualMachineScaleSetName: vmss.name + name: 'MicrosoftMonitoringAgent' + publisher: 'Microsoft.EnterpriseCloud.Monitoring' + type: osType == 'Windows' ? 'MicrosoftMonitoringAgent' : 'OmsAgentForLinux' + typeHandlerVersion: contains(extensionMonitoringAgentConfig, 'typeHandlerVersion') ? extensionMonitoringAgentConfig.typeHandlerVersion : (osType == 'Windows' ? '1.0' : '1.7') + 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 : '' + } + protectedSettings: { + workspaceKey: !empty(monitoringWorkspaceId) ? vmss_logAnalyticsWorkspace.listKeys().primarySharedKey : '' + } + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vmss_dependencyAgentExtension 'extensions/deploy.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vmss_networkWatcherAgentExtension 'extensions/deploy.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vmss_desiredStateConfigurationExtension 'extensions/deploy.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 : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vmss_customScriptExtension 'extensions/deploy.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 : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + vmss_desiredStateConfigurationExtension + ] +} + +module vmss_diskEncryptionExtension 'extensions/deploy.bicep' = if (extensionDiskEncryptionConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VMSS-DiskEncryption' + params: { + virtualMachineScaleSetName: vmss.name + name: 'DiskEncryption' + publisher: 'Microsoft.Azure.Security' + type: osType == 'Windows' ? 'AzureDiskEncryption' : 'AzureDiskEncryptionForLinux' + typeHandlerVersion: contains(extensionDiskEncryptionConfig, 'typeHandlerVersion') ? extensionDiskEncryptionConfig.typeHandlerVersion : (osType == 'Windows' ? '2.2' : '1.1') + autoUpgradeMinorVersion: contains(extensionDiskEncryptionConfig, 'autoUpgradeMinorVersion') ? extensionDiskEncryptionConfig.autoUpgradeMinorVersion : true + enableAutomaticUpgrade: contains(extensionDiskEncryptionConfig, 'enableAutomaticUpgrade') ? extensionDiskEncryptionConfig.enableAutomaticUpgrade : false + forceUpdateTag: contains(extensionDiskEncryptionConfig, 'forceUpdateTag') ? extensionDiskEncryptionConfig.forceUpdateTag : '1.0' + settings: extensionDiskEncryptionConfig.settings + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + vmss_customScriptExtension + vmss_microsoftMonitoringAgentExtension + ] +} + +resource vmss_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${vmss.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vmss +} + +resource vmss_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: publicIpDiagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: vmss +} + +module vmss_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VMSS-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: vmss.id + } +}] + +@description('The resource ID of the virtual machine scale set.') +output resourceId string = vmss.id + +@description('The resource group of the virtual machine scale set.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual machine scale set.') +output name string = vmss.name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(vmss.identity, 'principalId') ? vmss.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = vmss.location diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/deploy.bicep b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/deploy.bicep new file mode 100644 index 0000000..812188b --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/deploy.bicep @@ -0,0 +1,77 @@ +@description('Conditional. The name of the parent virtual machine scale set that extension is provisioned for. Required if the template is used in a standalone deployment.') +param virtualMachineScaleSetName string + +@description('Required. The name of the virtual machine scale set extension.') +param name string + +@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('Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false.') +param supressFailures bool = false + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualMachineScaleSet 'Microsoft.Compute/virtualMachineScaleSets@2021-07-01' existing = { + name: virtualMachineScaleSetName +} + +resource extension 'Microsoft.Compute/virtualMachineScaleSets/extensions@2021-07-01' = { + name: name + parent: virtualMachineScaleSet + 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 + suppressFailures: supressFailures + } +} + +@description('The name of the extension.') +output name string = extension.name + +@description('The ResourceId 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 diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/readme.md b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/readme.md new file mode 100644 index 0000000..2555364 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/readme.md @@ -0,0 +1,55 @@ +# virtual machine scale set Extensions `[Microsoft.Compute/virtualMachineScaleSets/extensions]` + +This module deploys a virtual machine scale set extension. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Compute/virtualMachineScaleSets/extensions` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/virtualMachineScaleSets/extensions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `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` | bool | Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available. | +| `name` | string | The name of the virtual machine scale set extension. | +| `publisher` | string | The name of the extension handler publisher. | +| `type` | string | Specifies the type of the extension; an example is "CustomScriptExtension". | +| `typeHandlerVersion` | string | Specifies the version of the script handler. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualMachineScaleSetName` | string | The name of the parent virtual machine scale set that extension is provisioned for. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `forceUpdateTag` | string | `''` | How the extension handler should be forced to update even if the extension configuration has not changed. | +| `protectedSettings` | secureObject | `{object}` | Any object that contains the extension specific protected settings. | +| `settings` | object | `{object}` | Any object that contains the extension specific settings. | +| `supressFailures` | bool | `False` | Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the extension. | +| `resourceGroupName` | string | The name of the Resource Group the extension was created in. | +| `resourceId` | string | The ResourceId of the extension. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/version.json b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/extensions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/readme.md b/modules/Microsoft.Compute/virtualMachineScaleSets/readme.md new file mode 100644 index 0000000..bb5f97d --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/readme.md @@ -0,0 +1,1830 @@ +# Virtual Machine Scale Sets `[Microsoft.Compute/virtualMachineScaleSets]` + +This module deploys a virtual machine scale set. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/virtualMachineScaleSets` | [2021-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-04-01/virtualMachineScaleSets) | +| `Microsoft.Compute/virtualMachineScaleSets/extensions` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/virtualMachineScaleSets/extensions) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +### Resource dependency + +The following resources are required to be able to deploy this resource. + +- `Microsoft.Network/VirtualNetwork` + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `adminUsername` | secureString | | Administrator username. | +| `imageReference` | object | | OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image. | +| `name` | string | | Name of the VMSS. | +| `nicConfigurations` | array | | Configures NICs and PIPs. | +| `osDisk` | object | | Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets. | +| `osType` | string | `[Linux, Windows]` | The chosen OS type. | +| `skuName` | string | | The SKU size of the VMs. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalUnattendContent` | array | `[]` | | Specifies additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. - AdditionalUnattendContent object. | +| `adminPassword` | secureString | `''` | | When specifying a Windows Virtual Machine, this value should be passed. | +| `automaticRepairsPolicyEnabled` | bool | `False` | | Specifies whether automatic repairs should be enabled on the virtual machine scale set. | +| `availabilityZones` | array | `[]` | | The virtual machine scale set zones. NOTE: Availability zones can only be set when you create the scale set. | +| `bootDiagnosticStorageAccountName` | string | `''` | | Storage account used to store boot diagnostic information. Boot diagnostics will be disabled if no value is provided. | +| `bootDiagnosticStorageAccountUri` | string | `[format('.blob.{0}/', environment().suffixes.storage)]` | | Storage account boot diagnostic base URI. | +| `customData` | string | `''` | | Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. | +| `dataDisks` | array | `[]` | | Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableAutomaticRollback` | bool | `False` | | Whether OS image rollback feature should be disabled. | +| `disablePasswordAuthentication` | bool | `False` | | Specifies whether password authentication should be disabled. | +| `doNotRunExtensionsOnOverprovisionedVMs` | bool | `False` | | When Overprovision is enabled, extensions are launched only on the requested number of VMs which are finally kept. This property will hence ensure that the extensions do not run on the extra overprovisioned VMs. | +| `enableAutomaticOSUpgrade` | bool | `False` | | Indicates whether OS upgrades should automatically be applied to scale set instances in a rolling fashion when a newer version of the OS image becomes available. Default value is false. If this is set to true for Windows based scale sets, enableAutomaticUpdates is automatically set to false and cannot be set to true. | +| `enableAutomaticUpdates` | bool | `True` | | Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableEvictionPolicy` | bool | `False` | | Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy. | +| `enableServerSideEncryption` | bool | `False` | | Specifies if Windows VM disks should be encrypted with Server-side encryption + Customer managed Key. | +| `encryptionAtHost` | bool | `True` | | This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your virtual machine scale sets. | +| `extensionAntiMalwareConfig` | object | `{object}` | | The configuration for the [Anti Malware] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionCustomScriptConfig` | object | `{object}` | | The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDependencyAgentConfig` | object | `{object}` | | The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDiskEncryptionConfig` | object | `{object}` | | The configuration for the [Disk Encryption] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDomainJoinConfig` | object | `{object}` | | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDomainJoinPassword` | secureString | `''` | | Required if name is specified. Password of the user specified in user parameter. | +| `extensionDSCConfig` | object | `{object}` | | The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionMonitoringAgentConfig` | object | `{object}` | | The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionNetworkWatcherAgentConfig` | object | `{object}` | | The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `gracePeriod` | string | `'PT30M'` | | The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M). | +| `licenseType` | string | `''` | `['', Windows_Client, Windows_Server]` | 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. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxBatchInstancePercent` | int | `20` | | 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` | string | `''` | | Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. | +| `maxUnhealthyInstancePercent` | int | `20` | | 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` | int | `20` | | 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` | string | `''` | | Resource ID of the monitoring log analytics workspace. | +| `overprovision` | bool | `False` | | Specifies whether the Virtual Machine Scale Set should be overprovisioned. | +| `pauseTimeBetweenBatches` | string | `'PT0S'` | | 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. | +| `plan` | object | `{object}` | | Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use. | +| `provisionVMAgent` | bool | `True` | | Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later. | +| `proximityPlacementGroupResourceId` | string | `''` | | Resource ID of a proximity placement group. | +| `publicIpDiagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `publicKeys` | array | `[]` | | The list of SSH public keys used to authenticate with linux based VMs. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sasTokenValidityLength` | string | `'PT8H'` | | SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | +| `scaleInPolicy` | object | `{object}` | | Specifies the scale-in policy that decides which virtual machines are chosen for removal when a Virtual Machine Scale Set is scaled-in. | +| `scaleSetFaultDomain` | int | `2` | | Fault Domain count for each placement group. | +| `scheduledEventsProfile` | object | `{object}` | | Specifies Scheduled Event related configurations. | +| `secrets` | array | `[]` | | Specifies set of certificates that should be installed onto the virtual machines in the scale set. | +| `secureBootEnabled` | bool | `False` | | Specifies whether secure boot should be enabled on the virtual machine scale set. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | +| `securityType` | string | `''` | | Specifies the SecurityType of the virtual machine scale set. It is set as TrustedLaunch to enable UefiSettings. | +| `singlePlacementGroup` | bool | `True` | | When true this limits the scale set to a single placement group, of max size 100 virtual machines. NOTE: If singlePlacementGroup is true, it may be modified to false. However, if singlePlacementGroup is false, it may not be modified to true. | +| `skuCapacity` | int | `1` | | The initial instance count of scale set VMs. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timeZone` | string | `''` | | Specifies the time zone of the virtual machine. e.g. 'Pacific Standard Time'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`. | +| `ultraSSDEnabled` | bool | `False` | | The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled. | +| `upgradePolicyMode` | string | `'Manual'` | `[Automatic, Manual, Rolling]` | Specifies the mode of an upgrade to virtual machines in the scale set.' Manual - You control the application of updates to virtual machines in the scale set. You do this by using the manualUpgrade action. ; Automatic - All virtual machines in the scale set are automatically updated at the same time. - Automatic, Manual, Rolling. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `vmNamePrefix` | string | `'vmssvm'` | | Specifies the computer name prefix for all of the virtual machines in the scale set. | +| `vmPriority` | string | `'Regular'` | `[Low, Regular, Spot]` | Specifies the priority for the virtual machine. | +| `vTpmEnabled` | bool | `False` | | Specifies whether vTPM should be enabled on the virtual machine scale set. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | +| `winRM` | object | `{object}` | | Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object. | +| `zoneBalance` | bool | `False` | | Whether to force strictly even Virtual Machine distribution cross x-zones in case there is zone outage. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Do not provide a value! This date value is used to generate a registration token. | + + +#### Marketplace images + +

+ +Parameter JSON format + +```json +"imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: '2016-Datacenter' + version: 'latest' +} +``` + +
+ +#### Custom images + +
+ +Parameter JSON format + +```json +"imageReference": { + "value": { + "id": "/subscriptions/12345-6789-1011-1213-15161718/resourceGroups/rg-name/providers/Microsoft.Compute/images/imagename" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +imageReference: { + id: '/subscriptions/12345-6789-1011-1213-15161718/resourceGroups/rg-name/providers/Microsoft.Compute/images/imagename' +} +``` + +
+

+ +### Parameter Usage: `plan` + +

+ +Parameter JSON format + +```json +"plan": { + "value": { + "name": "qvsa-25", + "product": "qualys-virtual-scanner", + "publisher": "qualysguard" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +plan: { + name: 'qvsa-25' + product: 'qualys-virtual-scanner' + publisher: 'qualysguard' +} +``` + +
+

+ +### Parameter Usage: `osDisk` + +

+ +Parameter JSON format + +```json +"osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS", + "diskEncryptionSet": { // Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets. + "id": "/subscriptions//resourceGroups//providers/Microsoft.Compute/diskEncryptionSets/" + } + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + diskEncryptionSet: { // Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VM Scale sets. + id: '/subscriptions//resourceGroups//providers/Microsoft.Compute/diskEncryptionSets/' + } + } +} +``` + +
+

+ +### Parameter Usage: `dataDisks` + +

+ +Parameter JSON format + +```json +"dataDisks": { + "value": [ + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "256", + "writeAcceleratorEnabled": true, + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "128", + "writeAcceleratorEnabled": true, + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +dataDisks: [ + { + caching: 'ReadOnly' + createOption: 'Empty' + diskSizeGB: '256' + writeAcceleratorEnabled: true + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + { + caching: 'ReadOnly' + createOption: 'Empty' + diskSizeGB: '128' + writeAcceleratorEnabled: true + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } +] +``` + +
+

+ +### Parameter Usage: `nicConfigurations` + +Comments: +- The field `nicSuffix` is mandatory. +- If not disabled, `enableAcceleratedNetworking` is considered `true` by default and requires the VMSS to be deployed with a supported OS and VM size. + +

+ +Parameter JSON format + +```json +"nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic01", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/agents-vmss-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-scaleset/subnets/sxx-az-subnet-scaleset-linux" + } + } + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +nicConfigurations: [ + { + nicSuffix: '-nic01' + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/agents-vmss-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-scaleset/subnets/sxx-az-subnet-scaleset-linux' + } + } + } + ] + } +] +``` + +
+

+ +### Parameter Usage: `extensionDomainJoinConfig` + +

+ +Parameter JSON format + +```json +"extensionDomainJoinConfig": { + "value": { + "enabled": true, + "settings": { + "name": "contoso.com", + "user": "test.user@testcompany.com", + "ouPath": "OU=testOU; DC=contoso; DC=com", + "restart": true, + "options": 3 + } + } +}, +"extensionDomainJoinPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/</resourceGroups/myRG/providers/Microsoft.KeyVault/vaults/myKvlt" + }, + "secretName": "domainJoinUser02-Password" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDomainJoinConfig: { + enabled: true + settings: { + name: 'contoso.com' + user: 'test.user@testcompany.com' + ouPath: 'OU=testOU; DC=contoso; DC=com' + restart: true + options: 3 + } +} + +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +extensionDomainJoinPassword: kv1.getSecret('domainJoinUser02-Password') +``` + +
+

+ +### Parameter Usage: `extensionNetworkWatcherAgentConfig` + +

+ +Parameter JSON format + +```json +"extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionNetworkWatcherAgentConfig: { + enabled: true +} +``` + +
+

+ +### Parameter Usage: `extensionAntiMalwareConfig` + +Only for OSType Windows + +

+ +Parameter JSON format + +```json +"extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": true, + "Exclusions": { + "Extensions": ".log;.ldf", + "Paths": "D:\\IISlogs;D:\\DatabaseLogs", + "Processes": "mssence.svc" + }, + "RealtimeProtectionEnabled": true, + "ScheduledScanSettings": { + "isEnabled": "true", + "scanType": "Quick", + "day": "7", + "time": "120" + } + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionAntiMalwareConfig: { + enabled: true + settings: { + AntimalwareEnabled: true + Exclusions: { + Extensions: '.log;.ldf' + Paths: 'D:\\IISlogs;D:\\DatabaseLogs' + Processes: 'mssence.svc' + } + RealtimeProtectionEnabled: true + ScheduledScanSettings: { + isEnabled: 'true' + scanType: 'Quick' + day: '7' + time: '120' + } + } +} +``` + +
+

+ +### Parameter Usage: `extensionDiskEncryptionConfig` + +

+ +Parameter JSON format + +```json +"extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://mykeyvault.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001", + "KeyEncryptionKeyURL": "https://mykeyvault.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", //'RSA-OAEP'/'RSA-OAEP-256'/'RSA1_5' + "VolumeType": "All", //'OS'/'Data'/'All' + "ResizeOSDisk": "false" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KeyVaultURL: 'https://mykeyvault.vault.azure.net/' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001' + KeyEncryptionKeyURL: 'https://mykeyvault.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' // ID must be updated for new keys + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' //'RSA-OAEP'/'RSA-OAEP-256'/'RSA1_5' + VolumeType: 'All' //'OS'/'Data'/'All' + ResizeOSDisk: 'false' + } +} +``` + +
+

+ +### Parameter Usage: `extensionCustomScriptConfig` + +

+ +Parameter JSON format + +```json +"extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + //storage accounts with SAS token requirement + { + "uri": "https://mystorageaccount.blob.core.windows.net/avdscripts/File1.ps1", + "storageAccountId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName" + }, + { + "uri": "https://mystorageaccount.blob.core.windows.net/avdscripts/File2.ps1", + "storageAccountId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName" + }, + //storage account with public container (no SAS token is required) OR other public URL (not a storage account) + { + "uri": "https://github.com/myProject/File3.ps1", + "storageAccountId": "" + } + ], + "settings": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File testscript.ps1" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionCustomScriptConfig: { + enabled: true + fileData: [ + //storage accounts with SAS token requirement + { + uri: 'https://mystorageaccount.blob.core.windows.net/avdscripts/File1.ps1' + storageAccountId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName' + } + { + uri: 'https://mystorageaccount.blob.core.windows.net/avdscripts/File2.ps1' + storageAccountId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName' + } + //storage account with public container (no SAS token is required) OR other public URL (not a storage account) + { + uri: 'https://github.com/myProject/File3.ps1' + storageAccountId: '' + } + ] + settings: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -File testscript.ps1' + } +} +``` + +
+

+ +### Parameter Usage: `extensionDSCConfig` + +

+ +Parameter JSON format + +```json +"extensionDSCConfig": { + "value": { + "enabled": true, + "settings": { + "wmfVersion": "latest", + "configuration": { + "url": "http://validURLToConfigLocation", + "script": "ConfigurationScript.ps1", + "function": "ConfigurationFunction" + }, + "configurationArguments": { + "argument1": "Value1", + "argument2": "Value2" + }, + "configurationData": { + "url": "https://foo.psd1" + }, + "privacy": { + "dataCollection": "enable" + }, + "advancedOptions": { + "forcePullAndApply": false, + "downloadMappings": { + "specificDependencyKey": "https://myCustomDependencyLocation" + } + } + }, + "protectedSettings": { + "configurationArguments": { + "mySecret": "MyPlaceholder" + }, + "configurationUrlSasToken": "MyPlaceholder", + "configurationDataUrlSasToken": "MyPlaceholder" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDSCConfig: { + enabled: true + settings: { + wmfVersion: 'latest' + configuration: { + url: 'http://validURLToConfigLocation' + script: 'ConfigurationScript.ps1' + function: 'ConfigurationFunction' + } + configurationArguments: { + argument1: 'Value1' + argument2: 'Value2' + } + configurationData: { + url: 'https://foo.psd1' + } + privacy: { + dataCollection: 'enable' + } + advancedOptions: { + forcePullAndApply: false + downloadMappings: { + specificDependencyKey: 'https://myCustomDependencyLocation' + } + } + } + protectedSettings: { + configurationArguments: { + mySecret: 'MyPlaceholder' + } + configurationUrlSasToken: 'MyPlaceholder' + configurationDataUrlSasToken: 'MyPlaceholder' + } +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual machine scale set. | +| `resourceGroupName` | string | The resource group of the virtual machine scale set. | +| `resourceId` | string | The resource ID of the virtual machine scale set. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Linux Min

+ +
+ +via Bicep module + +```bicep +module virtualMachineScaleSets './Microsoft.Compute/virtualMachineScaleSets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachineScaleSets' + params: { + // Required parameters + adminUsername: 'scaleSetAdmin' + imageReference: { + offer: 'UbuntuServer' + publisher: 'Canonical' + sku: '18.04-LTS' + version: 'latest' + } + name: '<>-scaleset-linux-min-001' + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + skuName: 'Standard_B12ms' + // Non-required parameters + disablePasswordAuthentication: true + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002' + } + } + } + ] + nicSuffix: '-nic01' + } + ] + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure' + path: '/home/scaleSetAdmin/.ssh/authorized_keys' + } + ] + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "scaleSetAdmin" + }, + "imageReference": { + "value": { + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "name": { + "value": "<>-scaleset-linux-min-001" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "skuName": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "disablePasswordAuthentication": { + "value": true + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, + "publicKeys": { + "value": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure", + "path": "/home/scaleSetAdmin/.ssh/authorized_keys" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Linux

+ +
+ +via Bicep module + +```bicep +module virtualMachineScaleSets './Microsoft.Compute/virtualMachineScaleSets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachineScaleSets' + params: { + // Required parameters + adminUsername: 'scaleSetAdmin' + imageReference: { + offer: 'UbuntuServer' + publisher: 'Canonical' + sku: '18.04-LTS' + version: 'latest' + } + name: '<>-scaleset-linux-001' + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + skuName: 'Standard_B12ms' + // Non-required parameters + availabilityZones: [ + '2' + ] + bootDiagnosticStorageAccountName: 'adp<>azsax001' + dataDisks: [ + { + caching: 'ReadOnly' + createOption: 'Empty' + diskSizeGB: '256' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + { + caching: 'ReadOnly' + createOption: 'Empty' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disablePasswordAuthentication: true + encryptionAtHost: false + extensionCustomScriptConfig: { + enabled: true + fileData: [ + { + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + uri: 'https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1' + } + ] + protectedSettings: { + commandToExecute: 'sudo apt-get update' + } + } + extensionDependencyAgentConfig: { + enabled: true + } + extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' + KeyEncryptionKeyURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyVaultURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/' + ResizeOSDisk: 'false' + VolumeType: 'All' + } + } + extensionMonitoringAgentConfig: { + enabled: true + } + extensionNetworkWatcherAgentConfig: { + enabled: true + } + lock: 'CanNotDelete' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002' + } + } + } + ] + nicSuffix: '-nic01' + } + ] + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure' + path: '/home/scaleSetAdmin/.ssh/authorized_keys' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + scaleSetFaultDomain: 1 + skuCapacity: 1 + systemAssignedIdentity: true + upgradePolicyMode: 'Manual' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + vmNamePrefix: 'vmsslinvm' + vmPriority: 'Regular' + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "scaleSetAdmin" + }, + "imageReference": { + "value": { + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "name": { + "value": "<>-scaleset-linux-001" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "skuName": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "availabilityZones": { + "value": [ + "2" + ] + }, + "bootDiagnosticStorageAccountName": { + "value": "adp<>azsax001" + }, + "dataDisks": { + "value": [ + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "256", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disablePasswordAuthentication": { + "value": true + }, + "encryptionAtHost": { + "value": false + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1" + } + ], + "protectedSettings": { + "commandToExecute": "sudo apt-get update" + } + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "ResizeOSDisk": "false", + "VolumeType": "All" + } + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "lock": { + "value": "CanNotDelete" + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, + "publicKeys": { + "value": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure", + "path": "/home/scaleSetAdmin/.ssh/authorized_keys" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scaleSetFaultDomain": { + "value": 1 + }, + "skuCapacity": { + "value": 1 + }, + "systemAssignedIdentity": { + "value": true + }, + "upgradePolicyMode": { + "value": "Manual" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "vmNamePrefix": { + "value": "vmsslinvm" + }, + "vmPriority": { + "value": "Regular" + } + } +} +``` + +
+

+ +

Example 3: Windows Min

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module virtualMachineScaleSets './Microsoft.Compute/virtualMachineScaleSets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachineScaleSets' + params: { + // Required parameters + adminUsername: kv1.getSecret('adminUsername') + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2016-Datacenter' + version: 'latest' + } + name: '<>-scaleset-win-min-001' + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + skuName: 'Standard_B12ms' + // Non-required parameters + adminPassword: kv1.getSecret('adminPassword') + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002' + } + } + } + ] + nicSuffix: '-nic01' + } + ] + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminUsername" + } + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } + }, + "name": { + "value": "<>-scaleset-win-min-001" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "skuName": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + } + } +} +``` + +
+

+ +

Example 4: Windows

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module virtualMachineScaleSets './Microsoft.Compute/virtualMachineScaleSets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachineScaleSets' + params: { + // Required parameters + adminUsername: kv1.getSecret('adminUsername') + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2016-Datacenter' + version: 'latest' + } + name: '<>-scaleset-win-001' + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + skuName: 'Standard_B12ms' + // Non-required parameters + adminPassword: kv1.getSecret('adminPassword') + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + encryptionAtHost: false + extensionAntiMalwareConfig: { + enabled: true + settings: { + AntimalwareEnabled: true + Exclusions: { + Extensions: '.log;.ldf' + Paths: 'D:\\IISlogs;D:\\DatabaseLogs' + Processes: 'mssence.svc' + } + RealtimeProtectionEnabled: true + ScheduledScanSettings: { + day: '7' + isEnabled: 'true' + scanType: 'Quick' + time: '120' + } + } + } + extensionCustomScriptConfig: { + enabled: true + fileData: [ + { + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + uri: 'https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1' + } + ] + protectedSettings: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -Command \'& .\\scriptExtensionMasterInstaller.ps1\'' + } + } + extensionDependencyAgentConfig: { + enabled: true + } + extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' + KeyEncryptionKeyURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyVaultURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/' + ResizeOSDisk: 'false' + VolumeType: 'All' + } + } + extensionDSCConfig: { + enabled: true + } + extensionMonitoringAgentConfig: { + enabled: true + } + extensionNetworkWatcherAgentConfig: { + enabled: true + } + lock: 'CanNotDelete' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002' + } + } + } + ] + nicSuffix: '-nic01' + } + ] + proximityPlacementGroupResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vmss-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuCapacity: 1 + systemAssignedIdentity: true + upgradePolicyMode: 'Manual' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + vmNamePrefix: 'vmsswinvm' + vmPriority: 'Regular' + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminUsername" + } + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } + }, + "name": { + "value": "<>-scaleset-win-001" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "skuName": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "encryptionAtHost": { + "value": false + }, + "extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": true, + "Exclusions": { + "Extensions": ".log;.ldf", + "Paths": "D:\\IISlogs;D:\\DatabaseLogs", + "Processes": "mssence.svc" + }, + "RealtimeProtectionEnabled": true, + "ScheduledScanSettings": { + "day": "7", + "isEnabled": "true", + "scanType": "Quick", + "time": "120" + } + } + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1" + } + ], + "protectedSettings": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"& .\\scriptExtensionMasterInstaller.ps1\"" + } + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "ResizeOSDisk": "false", + "VolumeType": "All" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": true + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "lock": { + "value": "CanNotDelete" + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-002" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, + "proximityPlacementGroupResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vmss-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuCapacity": { + "value": 1 + }, + "systemAssignedIdentity": { + "value": true + }, + "upgradePolicyMode": { + "value": "Manual" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "vmNamePrefix": { + "value": "vmsswinvm" + }, + "vmPriority": { + "value": "Regular" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/virtualMachineScaleSets/version.json b/modules/Microsoft.Compute/virtualMachineScaleSets/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachineScaleSets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Compute/virtualMachines/.bicep/nested_networkInterface.bicep b/modules/Microsoft.Compute/virtualMachines/.bicep/nested_networkInterface.bicep new file mode 100644 index 0000000..8709fd5 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.bicep/nested_networkInterface.bicep @@ -0,0 +1,99 @@ +param networkInterfaceName string +param virtualMachineName string +param location string +param tags object +param enableIPForwarding bool = false +param enableAcceleratedNetworking bool = false +param dnsServers array = [] + +@description('Optional. The network security group (NSG) to attach to the network interface.') +param networkSecurityGroupResourceId string = '' + +param ipConfigurations array +param lock string = '' +param diagnosticStorageAccountId string +param diagnosticLogsRetentionInDays int +param diagnosticWorkspaceId string +param diagnosticEventHubAuthorizationRuleId string +param diagnosticEventHubName string +param pipdiagnosticMetricsToEnable array +param pipdiagnosticLogCategoriesToEnable array +param nicDiagnosticMetricsToEnable array + +@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. The name of the PIP diagnostic setting, if deployed.') +param pipDiagnosticSettingsName string = '${virtualMachineName}-diagnosticSettings' + +@description('Optional. The name of the NIC diagnostic setting, if deployed.') +param nicDiagnosticSettingsName string = '${virtualMachineName}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +module networkInterface_publicIPAddresses '../../../Microsoft.Network/publicIPAddresses/deploy.bicep' = [for (ipConfiguration, index) in ipConfigurations: if (contains(ipConfiguration, 'pipconfiguration')) { + name: '${deployment().name}-publicIP-${index}' + params: { + name: '${virtualMachineName}${ipConfiguration.pipconfiguration.publicIpNameSuffix}' + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + diagnosticLogCategoriesToEnable: pipdiagnosticLogCategoriesToEnable + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticMetricsToEnable: pipdiagnosticMetricsToEnable + diagnosticSettingsName: pipDiagnosticSettingsName + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticWorkspaceId: diagnosticWorkspaceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: location + lock: lock + publicIPAddressVersion: contains(ipConfiguration, 'publicIPAddressVersion') ? ipConfiguration.publicIPAddressVersion : 'IPv4' + publicIPAllocationMethod: contains(ipConfiguration, 'publicIPAllocationMethod') ? ipConfiguration.publicIPAllocationMethod : 'Static' + publicIPPrefixResourceId: contains(ipConfiguration, 'publicIPPrefixResourceId') ? ipConfiguration.publicIPPrefixResourceId : '' + roleAssignments: contains(ipConfiguration, 'roleAssignments') ? ipConfiguration.roleAssignments : [] + skuName: contains(ipConfiguration, 'skuName') ? ipConfiguration.skuName : 'Standard' + skuTier: contains(ipConfiguration, 'skuTier') ? ipConfiguration.skuTier : 'Regional' + tags: tags + zones: contains(ipConfiguration, 'zones') ? ipConfiguration.zones : [] + } +}] + +module networkInterface '../../../Microsoft.Network/networkInterfaces/deploy.bicep' = { + name: '${deployment().name}-NetworkInterface' + params: { + name: networkInterfaceName + ipConfigurations: [for (ipConfiguration, index) in ipConfigurations: { + name: !empty(ipConfiguration.name) ? ipConfiguration.name : null + primary: index == 0 + privateIPAllocationMethod: contains(ipConfiguration, 'privateIPAllocationMethod') ? (!empty(ipConfiguration.privateIPAllocationMethod) ? ipConfiguration.privateIPAllocationMethod : null) : null + privateIPAddress: contains(ipConfiguration, 'privateIPAddress') ? (!empty(ipConfiguration.privateIPAddress) ? ipConfiguration.privateIPAddress : null) : null + publicIPAddressResourceId: contains(ipConfiguration, 'pipconfiguration') ? resourceId('Microsoft.Network/publicIPAddresses', '${virtualMachineName}${ipConfiguration.pipconfiguration.publicIpNameSuffix}') : null + subnetResourceId: ipConfiguration.subnetResourceId + loadBalancerBackendAddressPools: contains(ipConfiguration, 'loadBalancerBackendAddressPools') ? ipConfiguration.loadBalancerBackendAddressPools : null + applicationSecurityGroups: contains(ipConfiguration, 'applicationSecurityGroups') ? ipConfiguration.applicationSecurityGroups : null + applicationGatewayBackendAddressPools: contains(ipConfiguration, 'applicationGatewayBackendAddressPools') ? ipConfiguration.applicationGatewayBackendAddressPools : null + gatewayLoadBalancer: contains(ipConfiguration, 'gatewayLoadBalancer') ? ipConfiguration.gatewayLoadBalancer : null + loadBalancerInboundNatRules: contains(ipConfiguration, 'loadBalancerInboundNatRules') ? ipConfiguration.loadBalancerInboundNatRules : null + privateIPAddressVersion: contains(ipConfiguration, 'privateIPAddressVersion') ? ipConfiguration.privateIPAddressVersion : null + virtualNetworkTaps: contains(ipConfiguration, 'virtualNetworkTaps') ? ipConfiguration.virtualNetworkTaps : null + }] + location: location + tags: tags + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticMetricsToEnable: nicDiagnosticMetricsToEnable + diagnosticSettingsName: nicDiagnosticSettingsName + diagnosticWorkspaceId: diagnosticWorkspaceId + dnsServers: !empty(dnsServers) ? dnsServers : [] + enableAcceleratedNetworking: enableAcceleratedNetworking + enableDefaultTelemetry: enableReferencedModulesTelemetry + enableIPForwarding: enableIPForwarding + lock: lock + networkSecurityGroupResourceId: !empty(networkSecurityGroupResourceId) ? networkSecurityGroupResourceId : '' + roleAssignments: !empty(roleAssignments) ? roleAssignments : [] + } + dependsOn: [ + networkInterface_publicIPAddresses + ] +} diff --git a/modules/Microsoft.Compute/virtualMachines/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Compute/virtualMachines/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..8d55cc5 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'Microsoft OneAsset Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fd1bb084-1503-4bd2-99c0-630220046786') + '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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-07-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualMachine.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualMachine +}] diff --git a/modules/Microsoft.Compute/virtualMachines/.test/linux.autmg.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/linux.autmg.parameters.json new file mode 100644 index 0000000..f375587 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/linux.autmg.parameters.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-linux-autmg-01" + }, + "osType": { + "value": "Linux" + }, + "imageReference": { + "value": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "adminUsername": { + "value": "localAdminUser" + }, + "disablePasswordAuthentication": { + "value": true + }, + "publicKeys": { + "value": [ + { + "path": "/home/localAdminUser/.ssh/authorized_keys", + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure" + } + ] + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01" + } + } + ] + } + ] + }, + "configurationProfile": { + "value": "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction" + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/.test/linux.min.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/linux.min.parameters.json new file mode 100644 index 0000000..ac54d9a --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/linux.min.parameters.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-linux-min-01" + }, + "osType": { + "value": "Linux" + }, + "imageReference": { + "value": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "adminUsername": { + "value": "localAdminUser" + }, + "disablePasswordAuthentication": { + "value": true + }, + "publicKeys": { + "value": [ + { + "path": "/home/localAdminUser/.ssh/authorized_keys", + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure" + } + ] + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01" + } + } + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/.test/linux.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/linux.parameters.json new file mode 100644 index 0000000..05938fb --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/linux.parameters.json @@ -0,0 +1,218 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-linux-01" + }, + "lock": { + "value": "CanNotDelete" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "osType": { + "value": "Linux" + }, + "encryptionAtHost": { + "value": false + }, + "availabilityZone": { + "value": 1 + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "imageReference": { + "value": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "deleteOption": "Delete", + "caching": "ReadOnly", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "dataDisks": { + "value": [ + { + "createOption": "Empty", + "deleteOption": "Delete", + "caching": "ReadWrite", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "createOption": "Empty", + "deleteOption": "Delete", + "caching": "ReadWrite", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "adminUsername": { + "value": "localAdminUser" + }, + "disablePasswordAuthentication": { + "value": true + }, + "publicKeys": { + "value": [ + { + "path": "/home/localAdminUser/.ssh/authorized_keys", + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure" + } + ] + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "deleteOption": "Delete", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "backupVaultName": { + "value": "adp-<>-az-rsv-x-001" + }, + "backupVaultResourceGroup": { + "value": "validation-rg" + }, + "backupPolicyName": { + "value": "VMpolicy" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "monitoringWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "VolumeType": "All", + "ResizeOSDisk": "false" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": false + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1", + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ] + } + }, + "extensionCustomScriptProtectedSetting": { + "value": { + "commandToExecute": "sudo apt-get update" + } + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/.test/windows.autmg.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/windows.autmg.parameters.json new file mode 100644 index 0000000..cc63de2 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/windows.autmg.parameters.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-win-03" + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "adminUsername": { + "value": "localAdminUser" + }, + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + ] + }, + "configurationProfile": { + "value": "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction" + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/.test/windows.min.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/windows.min.parameters.json new file mode 100644 index 0000000..8537dea --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/windows.min.parameters.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-win-02" + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2022-datacenter-azure-edition", + "version": "latest" + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "adminUsername": { + "value": "localAdminUser" + }, + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/.test/windows.parameters.json b/modules/Microsoft.Compute/virtualMachines/.test/windows.parameters.json new file mode 100644 index 0000000..eea472a --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/.test/windows.parameters.json @@ -0,0 +1,238 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-vm-win-01" + }, + "lock": { + "value": "CanNotDelete" + }, + "encryptionAtHost": { + "value": false + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "deleteOption": "Delete", + "caching": "None", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "dataDisks": { + "value": [ + { + "createOption": "Empty", + "deleteOption": "Delete", + "caching": "None", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "createOption": "Empty", + "deleteOption": "Delete", + "caching": "None", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "availabilityZone": { + "value": 2 + }, + "adminUsername": { + "value": "localAdminUser" + }, + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "deleteOption": "Delete", + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "backupVaultName": { + "value": "adp-<>-az-rsv-x-001" + }, + "backupVaultResourceGroup": { + "value": "validation-rg" + }, + "backupPolicyName": { + "value": "VMpolicy" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "proximityPlacementGroupResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vm-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": "true", + "Exclusions": { + "Extensions": ".ext1;.ext2", + "Paths": "c:\\excluded-path-1;c:\\excluded-path-2", + "Processes": "excludedproc1.exe;excludedproc2.exe" + }, + "RealtimeProtectionEnabled": "true", + "ScheduledScanSettings": { + "isEnabled": "true", + "scanType": "Quick", + "day": "7", + "time": "120" + } + } + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "monitoringWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "VolumeType": "All", + "ResizeOSDisk": "false" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": true + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1", + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ] + } + }, + "extensionCustomScriptProtectedSetting": { + "value": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"& .\\scriptExtensionMasterInstaller.ps1\"" + } + } + } +} diff --git a/modules/Microsoft.Compute/virtualMachines/deploy.bicep b/modules/Microsoft.Compute/virtualMachines/deploy.bicep new file mode 100644 index 0000000..03df639 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/deploy.bicep @@ -0,0 +1,685 @@ +// Main resource +@description('Optional. The name of the virtual machine to be created. You should use a unique prefix to reduce name collisions in Active Directory. If no value is provided, a 10 character long unique string will be generated based on the Resource Group\'s name.') +param name string = take(toLower(uniqueString(resourceGroup().name)), 10) + +@description('Optional. Specifies whether the computer names should be transformed. The transformation is performed on all computer names. Available transformations are \'none\' (Default), \'uppercase\' and \'lowercase\'.') +@allowed([ + 'none' + 'uppercase' + 'lowercase' +]) +param vmComputerNamesTransformation string = 'none' + +@description('Required. Specifies the size for the VMs.') +param vmSize string + +@description('Optional. This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') +param encryptionAtHost bool = true + +@description('Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings.') +param securityType string = '' + +@description('Optional. Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings.') +param secureBootEnabled bool = false + +@description('Optional. Specifies whether vTPM should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings.') +param vTpmEnabled bool = false + +@description('Required. OS image reference. In case of marketplace images, it\'s the combination of the publisher, offer, sku, version attributes. In case of custom images it\'s the resource ID of the custom image.') +param imageReference object + +@description('Optional. Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use.') +param plan object = {} + +@description('Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') +param osDisk object + +@description('Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') +param dataDisks array = [] + +@description('Optional. The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled.') +param ultraSSDEnabled bool = false + +@description('Required. Administrator username.') +@secure() +param adminUsername string + +@description('Optional. When specifying a Windows Virtual Machine, this value should be passed.') +@secure() +param adminPassword string = '' + +@description('Optional. Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format.') +param customData string = '' + +@description('Optional. Specifies set of certificates that should be installed onto the virtual machine.') +param certificatesToBeInstalled array = [] + +@description('Optional. Specifies the priority for the virtual machine.') +@allowed([ + 'Regular' + 'Low' + 'Spot' +]) +param vmPriority string = 'Regular' + +@description('Optional. Specifies the eviction policy for the low priority virtual machine. Will result in \'Deallocate\' eviction policy.') +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 = '' + +@description('Optional. Specifies resource ID about the dedicated host that the virtual machine resides in.') +param dedicatedHostId string = '' + +@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([ + 'Windows_Client' + 'Windows_Server' + '' +]) +param licenseType string = '' + +@description('Optional. The list of SSH public keys used to authenticate with linux based VMs.') +param publicKeys array = [] + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Whether boot diagnostics should be enabled on the Virtual Machine. Boot diagnostics will be enabled with a managed storage account if no bootDiagnosticsStorageAccountName value is provided. If bootDiagnostics and bootDiagnosticsStorageAccountName values are not provided, boot diagnostics will be disabled.') +param bootDiagnostics bool = false + +@description('Optional. Custom storage account used to store boot diagnostic information. Boot diagnostics will be enabled with a custom storage account if a value is provided.') +param bootDiagnosticStorageAccountName string = '' + +@description('Optional. Storage account boot diagnostic base URI.') +param bootDiagnosticStorageAccountUri string = '.blob.${environment().suffixes.storage}/' + +@description('Optional. Resource ID of a proximity placement group.') +param proximityPlacementGroupResourceId string = '' + +@description('Optional. Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set.') +param availabilitySetResourceId string = '' + +@description('Optional. If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set.') +@allowed([ + 0 + 1 + 2 + 3 +]) +param availabilityZone int = 0 + +// External resources +@description('Required. Configures NICs and PIPs.') +param nicConfigurations array + +@description('Optional. The name of the PIP diagnostic setting, if deployed.') +param pipDiagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param pipdiagnosticLogCategoriesToEnable array = [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param pipdiagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the NIC diagnostic setting, if deployed.') +param nicDiagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param nicdiagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. Recovery service vault name to add VMs to backup.') +param backupVaultName string = '' + +@description('Optional. Resource group of the backup recovery service vault. If not provided the current resource group name is considered by default.') +param backupVaultResourceGroup string = resourceGroup().name + +@description('Optional. Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault.') +param backupPolicyName string = 'DefaultPolicy' + +@description('Optional. Specifies if Windows VM disks should be encrypted with Server-side encryption + Customer managed Key.') +param enableServerSideEncryption bool = false + +// 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 + +@description('Optional. Required if name is specified. Password of the user specified in user parameter.') +@secure() +param extensionDomainJoinPassword string = '' + +@description('Optional. The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDomainJoinConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Anti Malware] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionAntiMalwareConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionMonitoringAgentConfig object = { + enabled: false +} + +@description('Optional. Resource ID of the monitoring log analytics workspace. Must be set when extensionMonitoringAgentConfig is set to true.') +param monitoringWorkspaceId string = '' + +@description('Optional. The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDependencyAgentConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionNetworkWatcherAgentConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Disk Encryption] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDiskEncryptionConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionDSCConfig object = { + enabled: false +} + +@description('Optional. The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionCustomScriptConfig object = { + enabled: false + fileData: [] +} + +@description('Optional. Any object that contains the extension specific protected settings.') +@secure() +param extensionCustomScriptProtectedSetting object = {} + +// Shared parameters +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Generated. Do not provide a value! This date value is used to generate a registration token.') +param baseTime string = utcNow('u') + +@description('Optional. SAS token validity length to use to download files from storage accounts. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours.') +param sasTokenValidityLength string = 'PT8H' + +@description('Required. The chosen OS type.') +@allowed([ + 'Windows' + 'Linux' +]) +param osType string + +@description('Optional. Specifies whether password authentication should be disabled.') +#disable-next-line secure-secrets-in-params // Not a secret +param disablePasswordAuthentication bool = false + +@description('Optional. Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later.') +param provisionVMAgent bool = true + +@description('Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning.') +param enableAutomaticUpdates bool = true + +@description('Optional. Specifies the time zone of the virtual machine. e.g. \'Pacific Standard Time\'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`.') +param timeZone string = '' + +@description('Optional. Specifies additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. - AdditionalUnattendContent object.') +param additionalUnattendContent array = [] + +@description('Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object.') +param winRM object = {} + +@description('Required. The configuration profile of automanage.') +@allowed([ + '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' + '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' + '' +]) +param configurationProfile string = '' + +var vmComputerNameTransformed = vmComputerNamesTransformation == 'uppercase' ? toUpper(name) : (vmComputerNamesTransformation == 'lowercase' ? toLower(name) : name) + +var publicKeysFormatted = [for publicKey in publicKeys: { + path: publicKey.path + keyData: publicKey.keyData +}] + +var linuxConfiguration = { + disablePasswordAuthentication: disablePasswordAuthentication + ssh: { + publicKeys: publicKeysFormatted + } + provisionVMAgent: provisionVMAgent +} + +var windowsConfiguration = { + provisionVMAgent: provisionVMAgent + enableAutomaticUpdates: enableAutomaticUpdates + timeZone: empty(timeZone) ? null : timeZone + additionalUnattendContent: empty(additionalUnattendContent) ? null : additionalUnattendContent + winRM: !empty(winRM) ? { + listeners: winRM + } : null +} + +var accountSasProperties = { + signedServices: 'b' + signedPermission: 'r' + signedExpiry: dateTimeAdd(baseTime, sasTokenValidityLength) + signedResourceTypes: 'o' + signedProtocol: 'https' +} + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module vm_nic '.bicep/nested_networkInterface.bicep' = [for (nicConfiguration, index) in nicConfigurations: { + name: '${uniqueString(deployment().name, location)}-VM-Nic-${index}' + params: { + networkInterfaceName: '${name}${nicConfiguration.nicSuffix}' + virtualMachineName: name + location: location + tags: tags + enableIPForwarding: contains(nicConfiguration, 'enableIPForwarding') ? (!empty(nicConfiguration.enableIPForwarding) ? nicConfiguration.enableIPForwarding : false) : false + enableAcceleratedNetworking: contains(nicConfiguration, 'enableAcceleratedNetworking') ? nicConfiguration.enableAcceleratedNetworking : true + dnsServers: contains(nicConfiguration, 'dnsServers') ? (!empty(nicConfiguration.dnsServers) ? nicConfiguration.dnsServers : []) : [] + networkSecurityGroupResourceId: contains(nicConfiguration, 'networkSecurityGroupResourceId') ? nicConfiguration.networkSecurityGroupResourceId : '' + ipConfigurations: nicConfiguration.ipConfigurations + lock: lock + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + pipDiagnosticSettingsName: pipDiagnosticSettingsName + nicDiagnosticSettingsName: nicDiagnosticSettingsName + pipdiagnosticMetricsToEnable: pipdiagnosticMetricsToEnable + pipdiagnosticLogCategoriesToEnable: pipdiagnosticLogCategoriesToEnable + nicDiagnosticMetricsToEnable: nicdiagnosticMetricsToEnable + roleAssignments: contains(nicConfiguration, 'roleAssignments') ? (!empty(nicConfiguration.roleAssignments) ? nicConfiguration.roleAssignments : []) : [] + } +}] + +resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = { + name: name + location: location + identity: identity + tags: tags + zones: availabilityZone != 0 ? array(availabilityZone) : null + plan: !empty(plan) ? plan : null + properties: { + hardwareProfile: { + vmSize: vmSize + } + securityProfile: { + encryptionAtHost: encryptionAtHost ? encryptionAtHost : null + securityType: securityType + uefiSettings: securityType == 'TrustedLaunch' ? { + secureBootEnabled: secureBootEnabled + vTpmEnabled: vTpmEnabled + } : null + } + storageProfile: { + imageReference: imageReference + osDisk: { + name: '${name}-disk-os-01' + createOption: contains(osDisk, 'createOption') ? osDisk.createOption : 'FromImage' + deleteOption: contains(osDisk, 'deleteOption') ? osDisk.deleteOption : 'Delete' + diskSizeGB: osDisk.diskSizeGB + caching: contains(osDisk, 'caching') ? osDisk.caching : 'ReadOnly' + managedDisk: { + storageAccountType: osDisk.managedDisk.storageAccountType + diskEncryptionSet: contains(osDisk.managedDisk, 'diskEncryptionSet') ? osDisk.managedDisk.diskEncryptionSet : null + } + } + dataDisks: [for (dataDisk, index) in dataDisks: { + lun: index + name: '${name}-disk-data-${padLeft((index + 1), 2, '0')}' + diskSizeGB: dataDisk.diskSizeGB + createOption: contains(dataDisk, 'createOption') ? dataDisk.createOption : 'Empty' + deleteOption: contains(dataDisk, 'deleteOption') ? dataDisk.deleteOption : 'Delete' + caching: contains(dataDisk, 'caching') ? dataDisk.caching : 'ReadOnly' + managedDisk: { + storageAccountType: dataDisk.managedDisk.storageAccountType + diskEncryptionSet: { + id: enableServerSideEncryption ? dataDisk.managedDisk.diskEncryptionSet.id : null + } + } + }] + } + additionalCapabilities: { + ultraSSDEnabled: ultraSSDEnabled + } + osProfile: { + computerName: vmComputerNameTransformed + adminUsername: adminUsername + adminPassword: adminPassword + customData: !empty(customData) ? base64(customData) : null + windowsConfiguration: osType == 'Windows' ? windowsConfiguration : null + linuxConfiguration: osType == 'Linux' ? linuxConfiguration : null + secrets: certificatesToBeInstalled + allowExtensionOperations: allowExtensionOperations + } + networkProfile: { + networkInterfaces: [for (nicConfiguration, index) in nicConfigurations: { + properties: { + deleteOption: contains(nicConfiguration, 'deleteOption') ? nicConfiguration.deleteOption : 'Delete' + primary: index == 0 ? true : false + } + id: az.resourceId('Microsoft.Network/networkInterfaces', '${name}${nicConfiguration.nicSuffix}') + }] + } + diagnosticsProfile: { + bootDiagnostics: { + enabled: !empty(bootDiagnosticStorageAccountName) ? true : bootDiagnostics + storageUri: !empty(bootDiagnosticStorageAccountName) ? 'https://${bootDiagnosticStorageAccountName}${bootDiagnosticStorageAccountUri}' : null + } + } + availabilitySet: !empty(availabilitySetResourceId) ? { + id: availabilitySetResourceId + } : null + proximityPlacementGroup: !empty(proximityPlacementGroupResourceId) ? { + id: proximityPlacementGroupResourceId + } : null + priority: vmPriority + evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null + billingProfile: !empty(vmPriority) && !empty(maxPriceForLowPriorityVm) ? { + maxPrice: maxPriceForLowPriorityVm + } : null + host: !empty(dedicatedHostId) ? { + id: dedicatedHostId + } : null + licenseType: !empty(licenseType) ? licenseType : null + } + dependsOn: [ + vm_nic + ] +} + +resource vm_configurationProfileAssignment 'Microsoft.Automanage/configurationProfileAssignments@2021-04-30-preview' = if (!empty(configurationProfile)) { + name: 'default' + properties: { + configurationProfile: configurationProfile + } + scope: vm +} + +module vm_domainJoinExtension 'extensions/deploy.bicep' = if (extensionDomainJoinConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-DomainJoin' + params: { + virtualMachineName: vm.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 + } + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vm_microsoftAntiMalwareExtension 'extensions/deploy.bicep' = if (extensionAntiMalwareConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-MicrosoftAntiMalware' + params: { + virtualMachineName: vm.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource vm_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(monitoringWorkspaceId)) { + name: last(split(monitoringWorkspaceId, '/')) + scope: az.resourceGroup(split(monitoringWorkspaceId, '/')[2], split(monitoringWorkspaceId, '/')[4]) +} + +module vm_microsoftMonitoringAgentExtension 'extensions/deploy.bicep' = if (extensionMonitoringAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-MicrosoftMonitoringAgent' + params: { + virtualMachineName: vm.name + name: 'MicrosoftMonitoringAgent' + publisher: 'Microsoft.EnterpriseCloud.Monitoring' + type: osType == 'Windows' ? 'MicrosoftMonitoringAgent' : 'OmsAgentForLinux' + typeHandlerVersion: contains(extensionMonitoringAgentConfig, 'typeHandlerVersion') ? extensionMonitoringAgentConfig.typeHandlerVersion : (osType == 'Windows' ? '1.0' : '1.7') + autoUpgradeMinorVersion: contains(extensionMonitoringAgentConfig, 'autoUpgradeMinorVersion') ? extensionMonitoringAgentConfig.autoUpgradeMinorVersion : true + enableAutomaticUpgrade: contains(extensionMonitoringAgentConfig, 'enableAutomaticUpgrade') ? extensionMonitoringAgentConfig.enableAutomaticUpgrade : false + settings: { + workspaceId: !empty(monitoringWorkspaceId) ? reference(vm_logAnalyticsWorkspace.id, vm_logAnalyticsWorkspace.apiVersion).customerId : '' + } + protectedSettings: { + workspaceKey: !empty(monitoringWorkspaceId) ? vm_logAnalyticsWorkspace.listKeys().primarySharedKey : '' + } + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vm_dependencyAgentExtension 'extensions/deploy.bicep' = if (extensionDependencyAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-DependencyAgent' + params: { + virtualMachineName: vm.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vm_networkWatcherAgentExtension 'extensions/deploy.bicep' = if (extensionNetworkWatcherAgentConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-NetworkWatcherAgent' + params: { + virtualMachineName: vm.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 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vm_desiredStateConfigurationExtension 'extensions/deploy.bicep' = if (extensionDSCConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-DesiredStateConfiguration' + params: { + virtualMachineName: vm.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 : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module vm_customScriptExtension 'extensions/deploy.bicep' = if (extensionCustomScriptConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-CustomScriptExtension' + params: { + virtualMachineName: vm.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: extensionCustomScriptProtectedSetting + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + vm_desiredStateConfigurationExtension + ] +} + +module vm_diskEncryptionExtension 'extensions/deploy.bicep' = if (extensionDiskEncryptionConfig.enabled) { + name: '${uniqueString(deployment().name, location)}-VM-DiskEncryption' + params: { + virtualMachineName: vm.name + name: 'DiskEncryption' + publisher: 'Microsoft.Azure.Security' + type: osType == 'Windows' ? 'AzureDiskEncryption' : 'AzureDiskEncryptionForLinux' + typeHandlerVersion: contains(extensionDiskEncryptionConfig, 'typeHandlerVersion') ? extensionDiskEncryptionConfig.typeHandlerVersion : (osType == 'Windows' ? '2.2' : '1.1') + autoUpgradeMinorVersion: contains(extensionDiskEncryptionConfig, 'autoUpgradeMinorVersion') ? extensionDiskEncryptionConfig.autoUpgradeMinorVersion : true + enableAutomaticUpgrade: contains(extensionDiskEncryptionConfig, 'enableAutomaticUpgrade') ? extensionDiskEncryptionConfig.enableAutomaticUpgrade : false + forceUpdateTag: contains(extensionDiskEncryptionConfig, 'forceUpdateTag') ? extensionDiskEncryptionConfig.forceUpdateTag : '1.0' + settings: extensionDiskEncryptionConfig.settings + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + vm_customScriptExtension + vm_microsoftMonitoringAgentExtension + ] +} + +module vm_backup '../../Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/deploy.bicep' = if (!empty(backupVaultName)) { + name: '${uniqueString(deployment().name, location)}-VM-Backup' + params: { + name: 'vm;iaasvmcontainerv2;${resourceGroup().name};${vm.name}' + policyId: az.resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', backupVaultName, backupPolicyName) + protectedItemType: 'Microsoft.Compute/virtualMachines' + protectionContainerName: 'iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${vm.name}' + recoveryVaultName: backupVaultName + sourceResourceId: vm.id + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + scope: az.resourceGroup(backupVaultResourceGroup) + dependsOn: [ + vm_domainJoinExtension + vm_microsoftMonitoringAgentExtension + vm_microsoftAntiMalwareExtension + vm_networkWatcherAgentExtension + vm_dependencyAgentExtension + vm_desiredStateConfigurationExtension + vm_customScriptExtension + ] +} + +resource vm_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${vm.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vm +} + +module vm_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VM-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: vm.id + } +}] + +@description('The name of the VM.') +output name string = vm.name + +@description('The resource ID of the VM.') +output resourceId string = vm.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 systemAssignedPrincipalId string = systemAssignedIdentity && contains(vm.identity, 'principalId') ? vm.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = vm.location diff --git a/modules/Microsoft.Compute/virtualMachines/extensions/deploy.bicep b/modules/Microsoft.Compute/virtualMachines/extensions/deploy.bicep new file mode 100644 index 0000000..4a244a6 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/extensions/deploy.bicep @@ -0,0 +1,84 @@ +@description('Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment.') +param virtualMachineName string + +@description('Required. The name of the virtual 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('Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false.') +param supressFailures bool = false + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-07-01' existing = { + name: virtualMachineName +} + +resource extension 'Microsoft.Compute/virtualMachines/extensions@2021-07-01' = { + name: name + parent: virtualMachine + location: location + 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 + suppressFailures: supressFailures + } +} + +@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/modules/Microsoft.Compute/virtualMachines/extensions/readme.md b/modules/Microsoft.Compute/virtualMachines/extensions/readme.md new file mode 100644 index 0000000..06f4972 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/extensions/readme.md @@ -0,0 +1,57 @@ +# Virtual Machine Extensions `[Microsoft.Compute/virtualMachines/extensions]` + +This module deploys a virtual machine extension. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Compute/virtualMachines/extensions` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/virtualMachines/extensions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `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` | bool | Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available. | +| `name` | string | The name of the virtual machine extension. | +| `publisher` | string | The name of the extension handler publisher. | +| `type` | string | Specifies the type of the extension; an example is "CustomScriptExtension". | +| `typeHandlerVersion` | string | Specifies the version of the script handler. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualMachineName` | string | The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `forceUpdateTag` | string | `''` | How the extension handler should be forced to update even if the extension configuration has not changed. | +| `location` | string | `[resourceGroup().location]` | The location the extension is deployed to. | +| `protectedSettings` | secureObject | `{object}` | Any object that contains the extension specific protected settings. | +| `settings` | object | `{object}` | Any object that contains the extension specific settings. | +| `supressFailures` | bool | `False` | Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false. | + + +## Outputs + +| Output Name | 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_ diff --git a/modules/Microsoft.Compute/virtualMachines/extensions/version.json b/modules/Microsoft.Compute/virtualMachines/extensions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/extensions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Compute/virtualMachines/readme.md b/modules/Microsoft.Compute/virtualMachines/readme.md new file mode 100644 index 0000000..b6b0270 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/readme.md @@ -0,0 +1,2355 @@ +# Virtual Machines `[Microsoft.Compute/virtualMachines]` + +This module deploys one Virtual Machine with one or multiple nics and optionally one or multiple public IPs. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Automanage/configurationProfileAssignments` | [2021-04-30-preview](https://docs.microsoft.com/en-us/azure/templates) | +| `Microsoft.Compute/virtualMachines` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/virtualMachines) | +| `Microsoft.Compute/virtualMachines/extensions` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Compute/2021-07-01/virtualMachines/extensions) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/networkInterfaces` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkInterfaces) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers/protectedItems) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `adminUsername` | secureString | | | Administrator username. | +| `configurationProfile` | string | `''` | `['', /providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest, /providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction]` | The configuration profile of automanage. | +| `imageReference` | object | | | OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image. | +| `nicConfigurations` | array | | | Configures NICs and PIPs. | +| `osDisk` | object | | | Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | +| `osType` | string | | `[Linux, Windows]` | The chosen OS type. | +| `vmSize` | string | | | Specifies the size for the VMs. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalUnattendContent` | array | `[]` | | Specifies additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. - AdditionalUnattendContent object. | +| `adminPassword` | secureString | `''` | | When specifying a Windows Virtual Machine, this value should be passed. | +| `allowExtensionOperations` | bool | `True` | | 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. | +| `availabilitySetResourceId` | string | `''` | | Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set. | +| `availabilityZone` | int | `0` | `[0, 1, 2, 3]` | If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set. | +| `backupPolicyName` | string | `'DefaultPolicy'` | | Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault. | +| `backupVaultName` | string | `''` | | Recovery service vault name to add VMs to backup. | +| `backupVaultResourceGroup` | string | `[resourceGroup().name]` | | Resource group of the backup recovery service vault. If not provided the current resource group name is considered by default. | +| `bootDiagnostics` | bool | `False` | | Whether boot diagnostics should be enabled on the Virtual Machine. Boot diagnostics will be enabled with a managed storage account if no bootDiagnosticsStorageAccountName value is provided. If bootDiagnostics and bootDiagnosticsStorageAccountName values are not provided, boot diagnostics will be disabled. | +| `bootDiagnosticStorageAccountName` | string | `''` | | Custom storage account used to store boot diagnostic information. Boot diagnostics will be enabled with a custom storage account if a value is provided. | +| `bootDiagnosticStorageAccountUri` | string | `[format('.blob.{0}/', environment().suffixes.storage)]` | | Storage account boot diagnostic base URI. | +| `certificatesToBeInstalled` | array | `[]` | | Specifies set of certificates that should be installed onto the virtual machine. | +| `customData` | string | `''` | | Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. | +| `dataDisks` | array | `[]` | | Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | +| `dedicatedHostId` | string | `''` | | Specifies resource ID about the dedicated host that the virtual machine resides in. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disablePasswordAuthentication` | bool | `False` | | Specifies whether password authentication should be disabled. | +| `enableAutomaticUpdates` | bool | `True` | | Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableEvictionPolicy` | bool | `False` | | Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy. | +| `enableServerSideEncryption` | bool | `False` | | Specifies if Windows VM disks should be encrypted with Server-side encryption + Customer managed Key. | +| `encryptionAtHost` | bool | `True` | | This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | +| `extensionAntiMalwareConfig` | object | `{object}` | | The configuration for the [Anti Malware] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionCustomScriptConfig` | object | `{object}` | | The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionCustomScriptProtectedSetting` | secureObject | `{object}` | | Any object that contains the extension specific protected settings. | +| `extensionDependencyAgentConfig` | object | `{object}` | | The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDiskEncryptionConfig` | object | `{object}` | | The configuration for the [Disk Encryption] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDomainJoinConfig` | object | `{object}` | | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionDomainJoinPassword` | secureString | `''` | | Required if name is specified. Password of the user specified in user parameter. | +| `extensionDSCConfig` | object | `{object}` | | The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionMonitoringAgentConfig` | object | `{object}` | | The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `extensionNetworkWatcherAgentConfig` | object | `{object}` | | The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed. | +| `licenseType` | string | `''` | `['', Windows_Client, Windows_Server]` | 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. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxPriceForLowPriorityVm` | string | `''` | | Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars. | +| `monitoringWorkspaceId` | string | `''` | | Resource ID of the monitoring log analytics workspace. Must be set when extensionMonitoringAgentConfig is set to true. | +| `name` | string | `[take(toLower(uniqueString(resourceGroup().name)), 10)]` | | The name of the virtual machine to be created. You should use a unique prefix to reduce name collisions in Active Directory. If no value is provided, a 10 character long unique string will be generated based on the Resource Group's name. | +| `nicdiagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `nicDiagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the NIC diagnostic setting, if deployed. | +| `pipdiagnosticLogCategoriesToEnable` | array | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. | +| `pipdiagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `pipDiagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the PIP diagnostic setting, if deployed. | +| `plan` | object | `{object}` | | Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use. | +| `provisionVMAgent` | bool | `True` | | Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later. | +| `proximityPlacementGroupResourceId` | string | `''` | | Resource ID of a proximity placement group. | +| `publicKeys` | array | `[]` | | The list of SSH public keys used to authenticate with linux based VMs. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sasTokenValidityLength` | string | `'PT8H'` | | SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | +| `secureBootEnabled` | bool | `False` | | Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | +| `securityType` | string | `''` | | Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timeZone` | string | `''` | | Specifies the time zone of the virtual machine. e.g. 'Pacific Standard Time'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`. | +| `ultraSSDEnabled` | bool | `False` | | The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `vmComputerNamesTransformation` | string | `'none'` | `[lowercase, none, uppercase]` | Specifies whether the computer names should be transformed. The transformation is performed on all computer names. Available transformations are 'none' (Default), 'uppercase' and 'lowercase'. | +| `vmPriority` | string | `'Regular'` | `[Low, Regular, Spot]` | Specifies the priority for the virtual machine. | +| `vTpmEnabled` | bool | `False` | | Specifies whether vTPM should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | +| `winRM` | object | `{object}` | | Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Do not provide a value! This date value is used to generate a registration token. | + + +### Parameter Usage: `imageReference` + +#### Marketplace images + +

+ +Parameter JSON format + +```json +"imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + } +} +``` + +
+
+ +Bicep format + +```bicep +imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: '2016-Datacenter' + version: 'latest' +} +``` + +
+

+ +#### Custom images + +

+ +Parameter JSON format + +```json +"imageReference": { + "value": { + "id": "/subscriptions/12345-6789-1011-1213-15161718/resourceGroups/rg-name/providers/Microsoft.Compute/images/imagename" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +imageReference: { + id: '/subscriptions/12345-6789-1011-1213-15161718/resourceGroups/rg-name/providers/Microsoft.Compute/images/imagename' +} +``` + +
+

+ +### Parameter Usage: `plan` + +

+ +Parameter JSON format + +```json +"plan": { + "value": { + "name": "qvsa-25", + "product": "qualys-virtual-scanner", + "publisher": "qualysguard" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +plan: { + name: 'qvsa-25' + product: 'qualys-virtual-scanner' + publisher: 'qualysguard' +} +``` + +
+

+ +### Parameter Usage: `osDisk` + +

+ +Parameter JSON format + +```json +"osDisk": { + "value": { + "createOption": "fromImage", + "deleteOption": "Delete", // Optional. Can be 'Delete' or 'Detach' + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS", + "diskEncryptionSet": { // Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. + "id": "/subscriptions//resourceGroups//providers/Microsoft.Compute/diskEncryptionSets/" + } + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +osDisk: { + createOption: 'fromImage' + deleteOption: 'Delete' // Optional. Can be 'Delete' or 'Detach' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + diskEncryptionSet: { // Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. + id: '/subscriptions//resourceGroups//providers/Microsoft.Compute/diskEncryptionSets/' + } + } +} +``` + +
+

+ +### Parameter Usage: `dataDisks` + +

+ +Parameter JSON format + +```json +"dataDisks": { + "value": [ + { + "caching": "ReadOnly", + "createOption": "Empty", + "deleteOption": "Delete", // Optional. Can be 'Delete' or 'Detach' + "diskSizeGB": "256", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "ReadOnly", + "createOption": "Empty", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +dataDisks: [ + { + caching: 'ReadOnly' + createOption: 'Empty' + deleteOption: 'Delete' // Optional. Can be 'Delete' or 'Detach' + diskSizeGB: '256' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + { + caching: 'ReadOnly' + createOption: 'Empty' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } +] +``` + +
+

+ +### Parameter Usage: `nicConfigurations` + +Comments: +- The field `nicSuffix` and `subnetId` are mandatory. +- If `enablePublicIP` is set to true, then `publicIpNameSuffix` is also mandatory. +- Each IP config needs to have the mandatory field `name`. +- If not disabled, `enableAcceleratedNetworking` is considered `true` by default and requires the VM to be deployed with a supported OS and VM size. + +

+ +Parameter JSON format + +```json +"nicConfigurations": { + "value": [ + { + "nicSuffix": "-nic-01", + "deleteOption": "Delete", // Optional. Can be 'Delete' or 'Detach' + "ipConfigurations": [ + { + "name": "ipconfig1", + "subnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "" + ] + } + ] + } + }, + { + "name": "ipconfig2", + "subnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/", + } + ], + "nsgId": "/subscriptions//resourceGroups//providers/Microsoft.Network/networkSecurityGroups/", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "" + ] + } + ] + }, + { + "nicSuffix": "-nic-02", + "ipConfigurations": [ + { + "name": "ipconfig1", + "subnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-02" + } + }, + { + "name": "ipconfig2", + "subnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/", + "privateIPAllocationMethod": "Static", + "privateIPAddress": "10.0.0.9" + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +nicConfigurations: { + value: [ + { + nicSuffix: '-nic-01' + deleteOption: 'Delete' // Optional. Can be 'Delete' or 'Detach' + ipConfigurations: [ + { + name: 'ipconfig1' + subnetId: '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/' + pipConfiguration: { + publicIpNameSuffix: '-pip-01' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '' + ] + } + ] + } + } + { + name: 'ipconfig2' + subnetId: '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/' + } + ] + nsgId: '/subscriptions//resourceGroups//providers/Microsoft.Network/networkSecurityGroups/' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '' + ] + } + ] + } + { + nicSuffix: '-nic-02' + ipConfigurations: [ + { + name: 'ipconfig1' + subnetId: '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/' + pipConfiguration: { + publicIpNameSuffix: '-pip-02' + } + } + { + name: 'ipconfig2' + subnetId: '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/' + privateIPAllocationMethod: 'Static' + privateIPAddress: '10.0.0.9' + } + ] + } + ] +} +``` + +
+

+ +### Parameter Usage: `configurationProfileAssignments` + +

+ +Parameter JSON format + +```json +"configurationProfileAssignments": { + "value": [ + "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction", + "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +configurationProfileAssignments: [ + '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' + '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' +] +``` + +
+

+ +### Parameter Usage: `extensionDomainJoinConfig` + +

+ +Parameter JSON format + +```json +"extensionDomainJoinConfig": { + "value": { + "enabled": true, + "settings": { + "name": "contoso.com", + "user": "test.user@testcompany.com", + "ouPath": "OU=testOU; DC=contoso; DC=com", + "restart": true, + "options": 3 + } + } +}, +"extensionDomainJoinPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/</resourceGroups/myRG/providers/Microsoft.KeyVault/vaults/myKvlt" + }, + "secretName": "domainJoinUser02-Password" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDomainJoinConfig: { + enabled: true + settings: { + name: 'contoso.com' + user: 'test.user@testcompany.com' + ouPath: 'OU=testOU; DC=contoso; DC=com' + restart: true + options: 3 + } +} + +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +extensionDomainJoinPassword: kv1.getSecret('domainJoinUser02-Password') +``` + +
+

+ +### Parameter Usage: `extensionAntiMalwareConfig` + +Only for OSType Windows + +

+ +Parameter JSON format + +```json +"extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": true, + "Exclusions": { + "Extensions": ".log;.ldf", + "Paths": "D:\\IISlogs;D:\\DatabaseLogs", + "Processes": "mssence.svc" + }, + "RealtimeProtectionEnabled": true, + "ScheduledScanSettings": { + "isEnabled": "true", + "scanType": "Quick", + "day": "7", + "time": "120" + } + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionAntiMalwareConfig: { + enabled: true + settings: { + AntimalwareEnabled: true + Exclusions: { + Extensions: '.log;.ldf' + Paths: 'D:\\IISlogs;D:\\DatabaseLogs' + Processes: 'mssence.svc' + } + RealtimeProtectionEnabled: true + ScheduledScanSettings: { + isEnabled: 'true' + scanType: 'Quick' + day: '7' + time: '120' + } + } +} +``` + +
+

+ +### Parameter Usage: `extensionDiskEncryptionConfig` + +

+ +Parameter JSON format + +```json +"extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KeyVaultURL": "https://mykeyvault.vault.azure.net/", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001", + "KeyEncryptionKeyURL": "https://mykeyvault.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", // ID must be updated for new keys + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", //'RSA-OAEP'/'RSA-OAEP-256'/'RSA1_5' + "VolumeType": "All", //'OS'/'Data'/'All' + "ResizeOSDisk": "false" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KeyVaultURL: 'https://mykeyvault.vault.azure.net/' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001' + KeyEncryptionKeyURL: 'https://mykeyvault.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' // ID must be updated for new keys + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-sxx-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' //'RSA-OAEP'/'RSA-OAEP-256'/'RSA1_5' + VolumeType: 'All' //'OS'/'Data'/'All' + ResizeOSDisk: 'false' + } +} +``` + +
+

+ +### Parameter Usage: `extensionDSCConfig` + +

+ +Parameter JSON format + +```json +"extensionDSCConfig": { + "value": { + { + "enabled": true, + "settings": { + "wmfVersion": "latest", + "configuration": { + "url": "http://validURLToConfigLocation", + "script": "ConfigurationScript.ps1", + "function": "ConfigurationFunction" + }, + "configurationArguments": { + "argument1": "Value1", + "argument2": "Value2" + }, + "configurationData": { + "url": "https://foo.psd1" + }, + "privacy": { + "dataCollection": "enable" + }, + "advancedOptions": { + "forcePullAndApply": false, + "downloadMappings": { + "specificDependencyKey": "https://myCustomDependencyLocation" + } + } + }, + "protectedSettings": { + "configurationArguments": { + "mySecret": "MyPlaceholder" + }, + "configurationUrlSasToken": "MyPlaceholder", + "configurationDataUrlSasToken": "MyPlaceholder" + } + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionDSCConfig: { + { + enabled: true + settings: { + wmfVersion: 'latest' + configuration: { + url: 'http://validURLToConfigLocation' + script: 'ConfigurationScript.ps1' + function: 'ConfigurationFunction' + } + configurationArguments: { + argument1: 'Value1' + argument2: 'Value2' + } + configurationData: { + url: 'https://foo.psd1' + } + privacy: { + dataCollection: 'enable' + } + advancedOptions: { + forcePullAndApply: false + downloadMappings: { + specificDependencyKey: 'https://myCustomDependencyLocation' + } + } + } + protectedSettings: { + configurationArguments: { + mySecret: 'MyPlaceholder' + } + configurationUrlSasToken: 'MyPlaceholder' + configurationDataUrlSasToken: 'MyPlaceholder' + } + } +} +``` + +
+

+ +### Parameter Usage: `extensionCustomScriptConfig` + +

+ +Parameter JSON format + +```json +"extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + //storage accounts with SAS token requirement + { + "uri": "https://mystorageaccount.blob.core.windows.net/avdscripts/File1.ps1", + "storageAccountId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName" + }, + { + "uri": "https://mystorageaccount.blob.core.windows.net/avdscripts/File2.ps1", + "storageAccountId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName" + }, + //storage account with public container (no SAS token is required) OR other public URL (not a storage account) + { + "uri": "https://github.com/myProject/File3.ps1", + "storageAccountId": "" + } + ], + "settings": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File testscript.ps1" + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionCustomScriptConfig: { + enabled: true + fileData: [ + //storage accounts with SAS token requirement + { + uri: 'https://mystorageaccount.blob.core.windows.net/avdscripts/File1.ps1' + storageAccountId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName' + } + { + uri: 'https://mystorageaccount.blob.core.windows.net/avdscripts/File2.ps1' + storageAccountId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/storageAccountName' + } + //storage account with public container (no SAS token is required) OR other public URL (not a storage account) + { + uri: 'https://github.com/myProject/File3.ps1' + storageAccountId: '' + } + ] + settings: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -File testscript.ps1' + } +} +``` + +
+

+ +### Parameter Usage: `extensionCustomScriptProtectedSetting` + +This is used if you are going to use secrets or other sensitive information that you don't want to be visible in the deployment and logs. + +

+ +Parameter JSON format + +```json +"extensionCustomScriptProtectedSetting": { + "value": [ + { + "commandToExecute": "mycommandToRun -someParam MYSECRET" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +extensionCustomScriptProtectedSetting: [ + { + commandToExecute: 'mycommandToRun -someParam MYSECRET' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Considerations + +Enabling automanage triggers the creation of additional resources outside of the specific virtual machine deployment, such as: +- an `Automanage-Automate-` in the same Virtual Machine Resource Group and linking to the log analytics workspace leveraged by Azure Security Center. +- a `DefaultResourceGroup-` rg hosting a recovery services vault `DefaultBackupVault-` where vm backups are stored +For further details on automanage please refer to [Automanage virtual machines](https://docs.microsoft.com/en-us/azure/automanage/automanage-virtual-machines). + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the VM. | +| `resourceGroupName` | string | The name of the resource group the VM was created in. | +| `resourceId` | string | The resource ID of the VM. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/networkInterfaces` | Local reference | +| `Microsoft.Network/publicIPAddresses` | Local reference | +| `Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Linux Autmg

+ +
+ +via Bicep module + +```bicep +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'UbuntuServer' + publisher: 'Canonical' + sku: '18.04-LTS' + version: 'latest' + } + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + pipConfiguration: { + publicIpNameSuffix: '-pip-01' + } + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + vmSize: 'Standard_B12ms' + // Non-required parameters + configurationProfile: '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' + disablePasswordAuthentication: true + name: '<>-vm-linux-autmg-01' + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure' + path: '/home/localAdminUser/.ssh/authorized_keys' + } + ] + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig01", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01" + }, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01" + } + ] + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "configurationProfile": { + "value": "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction" + }, + "disablePasswordAuthentication": { + "value": true + }, + "name": { + "value": "<>-vm-linux-autmg-01" + }, + "publicKeys": { + "value": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure", + "path": "/home/localAdminUser/.ssh/authorized_keys" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Linux Min

+ +
+ +via Bicep module + +```bicep +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'UbuntuServer' + publisher: 'Canonical' + sku: '18.04-LTS' + version: 'latest' + } + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + pipConfiguration: { + publicIpNameSuffix: '-pip-01' + } + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + vmSize: 'Standard_B12ms' + // Non-required parameters + disablePasswordAuthentication: true + name: '<>-vm-linux-min-01' + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure' + path: '/home/localAdminUser/.ssh/authorized_keys' + } + ] + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig01", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01" + }, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01" + } + ] + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "disablePasswordAuthentication": { + "value": true + }, + "name": { + "value": "<>-vm-linux-min-01" + }, + "publicKeys": { + "value": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure", + "path": "/home/localAdminUser/.ssh/authorized_keys" + } + ] + } + } +} +``` + +
+

+ +

Example 3: Linux

+ +
+ +via Bicep module + +```bicep +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'UbuntuServer' + publisher: 'Canonical' + sku: '18.04-LTS' + version: 'latest' + } + nicConfigurations: [ + { + deleteOption: 'Delete' + ipConfigurations: [ + { + applicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + loadBalancerBackendAddressPools: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers' + } + ] + name: 'ipconfig01' + pipConfiguration: { + publicIpNameSuffix: '-pip-01' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + osDisk: { + caching: 'ReadOnly' + createOption: 'fromImage' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + vmSize: 'Standard_B12ms' + // Non-required parameters + availabilityZone: 1 + backupPolicyName: 'VMpolicy' + backupVaultName: 'adp-<>-az-rsv-x-001' + backupVaultResourceGroup: 'validation-rg' + dataDisks: [ + { + caching: 'ReadWrite' + createOption: 'Empty' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + { + caching: 'ReadWrite' + createOption: 'Empty' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disablePasswordAuthentication: true + encryptionAtHost: false + extensionCustomScriptConfig: { + enabled: true + fileData: [ + { + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + uri: 'https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1' + } + ] + } + extensionCustomScriptProtectedSetting: { + commandToExecute: 'sudo apt-get update' + } + extensionDependencyAgentConfig: { + enabled: true + } + extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' + KeyEncryptionKeyURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyVaultURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/' + ResizeOSDisk: 'false' + VolumeType: 'All' + } + } + extensionDSCConfig: { + enabled: false + } + extensionMonitoringAgentConfig: { + enabled: true + } + extensionNetworkWatcherAgentConfig: { + enabled: true + } + lock: 'CanNotDelete' + monitoringWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + name: '<>-vm-linux-01' + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure' + path: '/home/localAdminUser/.ssh/authorized_keys' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "deleteOption": "Delete", + "ipConfigurations": [ + { + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "name": "ipconfig01", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + ] + }, + "osDisk": { + "value": { + "caching": "ReadOnly", + "createOption": "fromImage", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Linux" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "availabilityZone": { + "value": 1 + }, + "backupPolicyName": { + "value": "VMpolicy" + }, + "backupVaultName": { + "value": "adp-<>-az-rsv-x-001" + }, + "backupVaultResourceGroup": { + "value": "validation-rg" + }, + "dataDisks": { + "value": [ + { + "caching": "ReadWrite", + "createOption": "Empty", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "ReadWrite", + "createOption": "Empty", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disablePasswordAuthentication": { + "value": true + }, + "encryptionAtHost": { + "value": false + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1" + } + ] + } + }, + "extensionCustomScriptProtectedSetting": { + "value": { + "commandToExecute": "sudo apt-get update" + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "ResizeOSDisk": "false", + "VolumeType": "All" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": false + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "lock": { + "value": "CanNotDelete" + }, + "monitoringWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "name": { + "value": "<>-vm-linux-01" + }, + "publicKeys": { + "value": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOir5eO28EBwxU0Dyra7g9h0HUXDyMNFp2z8PhaTUQgHjrimkMxjYRwEOG/lxnYL7+TqZk+HcPTfbZOunHBw0Wx2CITzILt6531vmIYZGfq5YyYXbxZa5MON7L/PVivoRlPj5Z/t4RhqMhyfR7EPcZ516LJ8lXPTo8dE/bkOCS+kFBEYHvPEEKAyLs19sRcK37SeHjpX04zdg62nqtuRr00Tp7oeiTXA1xn5K5mxeAswotmd8CU0lWUcJuPBWQedo649b+L2cm52kTncOBI6YChAeyEc1PDF0Tn9FmpdOWKtI9efh+S3f8qkcVEtSTXoTeroBd31nzjAunMrZeM8Ut6dre+XeQQIjT7I8oEm+ZkIuIyq0x2fls8JXP2YJDWDqu8v1+yLGTQ3Z9XVt2lMti/7bIgYxS0JvwOr5n5L4IzKvhb4fm13LLDGFa3o7Nsfe3fPb882APE0bLFCmfyIeiPh7go70WqZHakpgIr6LCWTyePez9CsI/rfWDb6eAM8= generated-by-azure", + "path": "/home/localAdminUser/.ssh/authorized_keys" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 4: Windows Autmg

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2019-Datacenter' + version: 'latest' + } + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + vmSize: 'Standard_B12ms' + // Non-required parameters + adminPassword: kv1.getSecret('adminPassword') + configurationProfile: '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' + name: '<>-vm-win-03' + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01" + } + ] + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "configurationProfile": { + "value": "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction" + }, + "name": { + "value": "<>-vm-win-03" + } + } +} +``` + +
+

+ +

Example 5: Windows Min

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2022-datacenter-azure-edition' + version: 'latest' + } + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + vmSize: 'Standard_B12ms' + // Non-required parameters + adminPassword: kv1.getSecret('adminPassword') + name: '<>-vm-win-02' + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2022-datacenter-azure-edition", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01" + } + ] + }, + "osDisk": { + "value": { + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "name": { + "value": "<>-vm-win-02" + } + } +} +``` + +
+

+ +

Example 6: Windows

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module virtualMachines './Microsoft.Compute/virtualMachines/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualMachines' + params: { + // Required parameters + adminUsername: 'localAdminUser' + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2019-Datacenter' + version: 'latest' + } + nicConfigurations: [ + { + deleteOption: 'Delete' + ipConfigurations: [ + { + applicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + loadBalancerBackendAddressPools: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers' + } + ] + name: 'ipconfig01' + pipConfiguration: { + publicIpNameSuffix: '-pip-01' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + nicSuffix: '-nic-01' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + osDisk: { + caching: 'None' + createOption: 'fromImage' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + vmSize: 'Standard_B12ms' + // Non-required parameters + adminPassword: kv1.getSecret('adminPassword') + availabilityZone: 2 + backupPolicyName: 'VMpolicy' + backupVaultName: 'adp-<>-az-rsv-x-001' + backupVaultResourceGroup: 'validation-rg' + dataDisks: [ + { + caching: 'None' + createOption: 'Empty' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + { + caching: 'None' + createOption: 'Empty' + deleteOption: 'Delete' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + encryptionAtHost: false + extensionAntiMalwareConfig: { + enabled: true + settings: { + AntimalwareEnabled: 'true' + Exclusions: { + Extensions: '.ext1;.ext2' + Paths: 'c:\\excluded-path-1;c:\\excluded-path-2' + Processes: 'excludedproc1.exe;excludedproc2.exe' + } + RealtimeProtectionEnabled: 'true' + ScheduledScanSettings: { + day: '7' + isEnabled: 'true' + scanType: 'Quick' + time: '120' + } + } + } + extensionCustomScriptConfig: { + enabled: true + fileData: [ + { + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + uri: 'https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1' + } + ] + } + extensionCustomScriptProtectedSetting: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -Command \'& .\\scriptExtensionMasterInstaller.ps1\'' + } + extensionDependencyAgentConfig: { + enabled: true + } + extensionDiskEncryptionConfig: { + enabled: true + settings: { + EncryptionOperation: 'EnableEncryption' + KekVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyEncryptionAlgorithm: 'RSA-OAEP' + KeyEncryptionKeyURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5' + KeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + KeyVaultURL: 'https://adp-<>-az-kv-x-001.vault.azure.net/' + ResizeOSDisk: 'false' + VolumeType: 'All' + } + } + extensionDSCConfig: { + enabled: true + } + extensionMonitoringAgentConfig: { + enabled: true + } + extensionNetworkWatcherAgentConfig: { + enabled: true + } + lock: 'CanNotDelete' + monitoringWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + name: '<>-vm-win-01' + proximityPlacementGroupResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vm-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "adminUsername": { + "value": "localAdminUser" + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + } + }, + "nicConfigurations": { + "value": [ + { + "deleteOption": "Delete", + "ipConfigurations": [ + { + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "name": "ipconfig01", + "pipConfiguration": { + "publicIpNameSuffix": "-pip-01", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ], + "nicSuffix": "-nic-01", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + ] + }, + "osDisk": { + "value": { + "caching": "None", + "createOption": "fromImage", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "vmSize": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "adminPassword" + } + }, + "availabilityZone": { + "value": 2 + }, + "backupPolicyName": { + "value": "VMpolicy" + }, + "backupVaultName": { + "value": "adp-<>-az-rsv-x-001" + }, + "backupVaultResourceGroup": { + "value": "validation-rg" + }, + "dataDisks": { + "value": [ + { + "caching": "None", + "createOption": "Empty", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + { + "caching": "None", + "createOption": "Empty", + "deleteOption": "Delete", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "encryptionAtHost": { + "value": false + }, + "extensionAntiMalwareConfig": { + "value": { + "enabled": true, + "settings": { + "AntimalwareEnabled": "true", + "Exclusions": { + "Extensions": ".ext1;.ext2", + "Paths": "c:\\excluded-path-1;c:\\excluded-path-2", + "Processes": "excludedproc1.exe;excludedproc2.exe" + }, + "RealtimeProtectionEnabled": "true", + "ScheduledScanSettings": { + "day": "7", + "isEnabled": "true", + "scanType": "Quick", + "time": "120" + } + } + } + }, + "extensionCustomScriptConfig": { + "value": { + "enabled": true, + "fileData": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "uri": "https://adp<>azsax001.blob.core.windows.net/scripts/scriptExtensionMasterInstaller.ps1" + } + ] + } + }, + "extensionCustomScriptProtectedSetting": { + "value": { + "commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"& .\\scriptExtensionMasterInstaller.ps1\"" + } + }, + "extensionDependencyAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionDiskEncryptionConfig": { + "value": { + "enabled": true, + "settings": { + "EncryptionOperation": "EnableEncryption", + "KekVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyEncryptionAlgorithm": "RSA-OAEP", + "KeyEncryptionKeyURL": "https://adp-<>-az-kv-x-001.vault.azure.net/keys/keyEncryptionKey/bc3bb46d95c64367975d722f473eeae5", + "KeyVaultResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001", + "KeyVaultURL": "https://adp-<>-az-kv-x-001.vault.azure.net/", + "ResizeOSDisk": "false", + "VolumeType": "All" + } + } + }, + "extensionDSCConfig": { + "value": { + "enabled": true + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true + } + }, + "extensionNetworkWatcherAgentConfig": { + "value": { + "enabled": true + } + }, + "lock": { + "value": "CanNotDelete" + }, + "monitoringWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "name": { + "value": "<>-vm-win-01" + }, + "proximityPlacementGroupResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/proximityPlacementGroups/adp-<>-az-ppg-vm-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Compute/virtualMachines/version.json b/modules/Microsoft.Compute/virtualMachines/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Compute/virtualMachines/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Consumption/budgets/.test/parameters.json b/modules/Microsoft.Consumption/budgets/.test/parameters.json new file mode 100644 index 0000000..0534e32 --- /dev/null +++ b/modules/Microsoft.Consumption/budgets/.test/parameters.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "Monthly-Cost-Budget" + }, + "amount": { + "value": 500 + }, + "thresholds": { + "value": [ + 50, + 75, + 90, + 100, + 110 + ] + }, + "contactEmails": { + "value": [ + "dummy@contoso.com" + ] + } + } +} diff --git a/modules/Microsoft.Consumption/budgets/deploy.bicep b/modules/Microsoft.Consumption/budgets/deploy.bicep new file mode 100644 index 0000000..439d6e4 --- /dev/null +++ b/modules/Microsoft.Consumption/budgets/deploy.bicep @@ -0,0 +1,107 @@ +targetScope = 'subscription' + +@description('Required. The name of the budget.') +param name string + +@allowed([ + 'Cost' + 'Usage' +]) +@description('Optional. The category of the budget, whether the budget tracks cost or usage.') +param category string = 'Cost' + +@description('Required. The total amount of cost or usage to track with the budget.') +param amount int + +@allowed([ + 'Monthly' + 'Quarterly' + 'Annually' + 'BillingMonth' + 'BillingQuarter' + 'BillingAnnual' +]) +@description('Optional. The time covered by a budget. Tracking of the amount will be reset based on the time grain. BillingMonth, BillingQuarter, and BillingAnnual are only supported by WD customers.') +param resetPeriod string = 'Monthly' + +@description('Optional. The start date for the budget. Start date should be the first day of the month and cannot be in the past (except for the current month).') +param startDate string = '${utcNow('yyyy')}-${utcNow('MM')}-01T00:00:00Z' + +@description('Optional. The end date for the budget. If not provided, it will default to 10 years from the start date.') +param endDate string = '' + +@maxLength(5) +@description('Optional. Percent thresholds of budget for when to get a notification. Can be up to 5 thresholds, where each must be between 1 and 1000.') +param thresholds array = [ + 50 + 75 + 90 + 100 + 110 +] + +@description('Optional. The list of email addresses to send the budget notification to when the thresholds are exceeded.') +param contactEmails array = [] + +@description('Optional. The list of contact roles to send the budget notification to when the thresholds are exceeded.') +param contactRoles array = [] + +@description('Optional. List of action group resource IDs that will receive the alert.') +param actionGroups array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +var notificationsArray = [for threshold in thresholds: { + 'Actual_GreaterThan_${threshold}_Percentage': { + enabled: true + operator: 'GreaterThan' + threshold: threshold + contactEmails: empty(contactEmails) ? null : array(contactEmails) + contactRoles: empty(contactRoles) ? null : array(contactRoles) + contactGroups: empty(actionGroups) ? null : array(actionGroups) + thresholdType: 'Actual' + } +}] + +var notifications = json(replace(replace(replace(string(notificationsArray), '[{', '{'), '}]', '}'), '}},{', '},')) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource budget 'Microsoft.Consumption/budgets@2019-05-01' = { + name: name + properties: { + category: category + amount: amount + timeGrain: resetPeriod + timePeriod: { + startDate: startDate + endDate: endDate + } + filter: {} + notifications: notifications + } +} + +@description('The name of the budget.') +output name string = budget.name + +@description('The resource ID of the budget.') +output resourceId string = budget.id + +@description('The subscription the budget was deployed into.') +output subscriptionName string = subscription().displayName diff --git a/modules/Microsoft.Consumption/budgets/readme.md b/modules/Microsoft.Consumption/budgets/readme.md new file mode 100644 index 0000000..f88ecb0 --- /dev/null +++ b/modules/Microsoft.Consumption/budgets/readme.md @@ -0,0 +1,128 @@ +# Budgets `[Microsoft.Consumption/budgets]` + +This module deploys budgets for subscriptions. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Consumption/budgets` | [2019-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Consumption/2019-05-01/budgets) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `amount` | int | The total amount of cost or usage to track with the budget. | +| `name` | string | The name of the budget. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actionGroups` | array | `[]` | | List of action group resource IDs that will receive the alert. | +| `category` | string | `'Cost'` | `[Cost, Usage]` | The category of the budget, whether the budget tracks cost or usage. | +| `contactEmails` | array | `[]` | | The list of email addresses to send the budget notification to when the thresholds are exceeded. | +| `contactRoles` | array | `[]` | | The list of contact roles to send the budget notification to when the thresholds are exceeded. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `endDate` | string | `''` | | The end date for the budget. If not provided, it will default to 10 years from the start date. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `resetPeriod` | string | `'Monthly'` | `[Annually, BillingAnnual, BillingMonth, BillingQuarter, Monthly, Quarterly]` | The time covered by a budget. Tracking of the amount will be reset based on the time grain. BillingMonth, BillingQuarter, and BillingAnnual are only supported by WD customers. | +| `startDate` | string | `[format('{0}-{1}-01T00:00:00Z', utcNow('yyyy'), utcNow('MM'))]` | | The start date for the budget. Start date should be the first day of the month and cannot be in the past (except for the current month). | +| `thresholds` | array | `[50, 75, 90, 100, 110]` | | Percent thresholds of budget for when to get a notification. Can be up to 5 thresholds, where each must be between 1 and 1000. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the budget. | +| `resourceId` | string | The resource ID of the budget. | +| `subscriptionName` | string | The subscription the budget was deployed into. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module budgets './Microsoft.Consumption/budgets/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Budgets' + params: { + // Required parameters + amount: 500 + name: 'Monthly-Cost-Budget' + // Non-required parameters + contactEmails: [ + 'dummy@contoso.com' + ] + thresholds: [ + 50 + 75 + 90 + 100 + 110 + ] + } +} +``` + +
+

+ +

+ +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 + "amount": { + "value": 500 + }, + "name": { + "value": "Monthly-Cost-Budget" + }, + // Non-required parameters + "contactEmails": { + "value": [ + "dummy@contoso.com" + ] + }, + "thresholds": { + "value": [ + 50, + 75, + 90, + 100, + 110 + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Consumption/budgets/version.json b/modules/Microsoft.Consumption/budgets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Consumption/budgets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ContainerInstance/containerGroups/.test/parameters.json b/modules/Microsoft.ContainerInstance/containerGroups/.test/parameters.json new file mode 100644 index 0000000..cdbb107 --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/.test/parameters.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-acg-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "containerName": { + "value": "<>-az-aci-x-001" + }, + "image": { + "value": "mcr.microsoft.com/azuredocs/aci-helloworld" + }, + "ports": { + "value": [ + { + "protocol": "Tcp", + "port": "80" + }, + { + "protocol": "Tcp", + "port": "443" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} diff --git a/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep b/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep new file mode 100644 index 0000000..4574849 --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep @@ -0,0 +1,139 @@ +@description('Required. Name for the container group.') +param name string + +@description('Required. Name for the container.') +param containername string + +@description('Required. Name of the image.') +param image string + +@description('Optional. Port to open on the container and the public IP address.') +param ports array = [ + { + protocol: 'TCP' + port: '443' + } +] + +@description('Optional. The number of CPU cores to allocate to the container.') +param cpuCores int = 2 + +@description('Optional. The amount of memory to allocate to the container in gigabytes.') +param memoryInGB int = 2 + +@description('Optional. The operating system type required by the containers in the container group. - Windows or Linux.') +param osType string = 'Linux' + +@description('Optional. Restart policy for all containers within the container group. - Always: Always restart. OnFailure: Restart on failure. Never: Never restart. - Always, OnFailure, Never.') +param restartPolicy string = 'Always' + +@description('Optional. Specifies if the IP is exposed to the public internet or private VNET. - Public or Private.') +param ipAddressType string = 'Public' + +@description('Optional. The image registry credentials by which the container group is created from.') +param imageRegistryCredentials array = [] + +@description('Optional. Environment variables of the container group.') +param environmentVariables array = [] + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource containergroup 'Microsoft.ContainerInstance/containerGroups@2021-10-01' = { + name: name + location: location + identity: identity + tags: tags + properties: { + containers: [ + { + name: containername + properties: { + command: [] + image: image + ports: ports + resources: { + requests: { + cpu: cpuCores + memoryInGB: memoryInGB + } + } + environmentVariables: environmentVariables + } + } + ] + imageRegistryCredentials: imageRegistryCredentials + restartPolicy: restartPolicy + osType: osType + ipAddress: { + type: ipAddressType + ports: ports + } + } +} + +resource containergroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${containergroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: containergroup +} + +@description('The name of the container group.') +output name string = containergroup.name + +@description('The resource ID of the container group.') +output resourceId string = containergroup.id + +@description('The resource group the container group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The IPv4 address of the container group.') +output iPv4Address string = containergroup.properties.ipAddress.ip + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(containergroup.identity, 'principalId') ? containergroup.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = containergroup.location diff --git a/modules/Microsoft.ContainerInstance/containerGroups/readme.md b/modules/Microsoft.ContainerInstance/containerGroups/readme.md new file mode 100644 index 0000000..982cb4f --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/readme.md @@ -0,0 +1,268 @@ +# Container Instances `[Microsoft.ContainerInstance/containerGroups]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +### Container groups in Azure Container Instances + +The top-level resource in Azure Container Instances is the container group. A container group is a collection of containers that get scheduled on the same host machine. The containers in a container group share a lifecycle, resources, local network, and storage volumes. It's similar in concept to a pod in Kubernetes. + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.ContainerInstance/containerGroups` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerInstance/2021-10-01/containerGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `containername` | string | Name for the container. | +| `image` | string | Name of the image. | +| `name` | string | Name for the container group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `cpuCores` | int | `2` | | The number of CPU cores to allocate to the container. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `environmentVariables` | array | `[]` | | Environment variables of the container group. | +| `imageRegistryCredentials` | array | `[]` | | The image registry credentials by which the container group is created from. | +| `ipAddressType` | string | `'Public'` | | Specifies if the IP is exposed to the public internet or private VNET. - Public or Private. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `memoryInGB` | int | `2` | | The amount of memory to allocate to the container in gigabytes. | +| `osType` | string | `'Linux'` | | The operating system type required by the containers in the container group. - Windows or Linux. | +| `ports` | array | `[System.Collections.Hashtable]` | | Port to open on the container and the public IP address. | +| `restartPolicy` | string | `'Always'` | | Restart policy for all containers within the container group. - Always: Always restart. OnFailure: Restart on failure. Never: Never restart. - Always, OnFailure, Never. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `imageRegistryCredentials` + +The image registry credentials by which the container group is created from. + +

+ +Parameter JSON format + +```json +"imageRegistryCredentials": { + "value": [ + { + "server": "sxxazacrx001.azurecr.io", + "username": "sxxazacrx001" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +imageRegistryCredentials: [ + { + server: 'sxxazacrx001.azurecr.io' + username: 'sxxazacrx001' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `iPv4Address` | string | The IPv4 address of the container group. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the container group. | +| `resourceGroupName` | string | The resource group the container group was deployed into. | +| `resourceId` | string | The resource ID of the container group. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module containerGroups './Microsoft.ContainerInstance/containerGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ContainerGroups' + params: { + // Required parameters + containerName: '<>-az-aci-x-001' + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + name: '<>-az-acg-x-001' + // Non-required parameters + lock: 'CanNotDelete' + ports: [ + { + port: '80' + protocol: 'Tcp' + } + { + port: '443' + protocol: 'Tcp' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "containerName": { + "value": "<>-az-aci-x-001" + }, + "image": { + "value": "mcr.microsoft.com/azuredocs/aci-helloworld" + }, + "name": { + "value": "<>-az-acg-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "ports": { + "value": [ + { + "port": "80", + "protocol": "Tcp" + }, + { + "port": "443", + "protocol": "Tcp" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ContainerInstance/containerGroups/version.json b/modules/Microsoft.ContainerInstance/containerGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ContainerRegistry/registries/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ContainerRegistry/registries/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a9b24d3 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,77 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'AcrDelete': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11') + 'AcrImageSigner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f') + 'AcrPull': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + 'AcrPush': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec') + 'AcrQuarantineReader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04') + 'AcrQuarantineWriter': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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 registry 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(registry.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: registry +}] diff --git a/modules/Microsoft.ContainerRegistry/registries/.test/encr.parameters.json b/modules/Microsoft.ContainerRegistry/registries/.test/encr.parameters.json new file mode 100644 index 0000000..2ed76c3 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/.test/encr.parameters.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azacrencr001" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "acrSku": { + "value": "Premium" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + } + } +} diff --git a/modules/Microsoft.ContainerRegistry/registries/.test/min.parameters.json b/modules/Microsoft.ContainerRegistry/registries/.test/min.parameters.json new file mode 100644 index 0000000..255a9dd --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/.test/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": "<>azacrmin001" + } + } +} diff --git a/modules/Microsoft.ContainerRegistry/registries/.test/parameters.json b/modules/Microsoft.ContainerRegistry/registries/.test/parameters.json new file mode 100644 index 0000000..518df21 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/.test/parameters.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azacrx002" + }, + "lock": { + "value": "CanNotDelete" + }, + "acrAdminUserEnabled": { + "value": false + }, + "acrSku": { + "value": "Premium" + }, + "exportPolicyStatus": { + "value": "enabled" + }, + "quarantinePolicyStatus": { + "value": "enabled" + }, + "trustPolicyStatus": { + "value": "enabled" + }, + "azureADAuthenticationAsArmPolicyStatus": { + "value": "enabled" + }, + "softDeletePolicyStatus": { + "value": "disabled" + }, + "softDeletePolicyDays": { + "value": 7 + }, + "replications": { + "value": [ + { + "name": "northeurope", + "location": "northeurope" + } + ] + }, + "webhooks": { + "value": [ + { + "name": "<>azacrx001webhook", + "serviceUri": "https://www.contoso.com/webhook" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "networkRuleSetIpRules": { + "value": [ + { + "action": "Allow", + "value": "40.74.28.0/23" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "registry", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.ContainerRegistry/registries/.test/pe.parameters.json b/modules/Microsoft.ContainerRegistry/registries/.test/pe.parameters.json new file mode 100644 index 0000000..bef24d0 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/.test/pe.parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azacrpe001" + }, + "acrSku": { + "value": "Premium" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "registry", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.ContainerRegistry/registries/deploy.bicep b/modules/Microsoft.ContainerRegistry/registries/deploy.bicep new file mode 100644 index 0000000..1b04b44 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/deploy.bicep @@ -0,0 +1,391 @@ +@description('Required. Name of your Azure container registry.') +@minLength(5) +@maxLength(50) +param name string + +@description('Optional. Enable admin user that have push / pull permission to the registry.') +param acrAdminUserEnabled bool = false + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@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. Tier of your Azure container registry.') +@allowed([ + 'Basic' + 'Premium' + 'Standard' +]) +param acrSku string = 'Basic' + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The value that indicates whether the export policy is enabled or not.') +param exportPolicyStatus string = 'disabled' + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The value that indicates whether the quarantine policy is enabled or not.') +param quarantinePolicyStatus string = 'disabled' + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The value that indicates whether the trust policy is enabled or not.') +param trustPolicyStatus string = 'disabled' + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The value that indicates whether the retention policy is enabled or not.') +param retentionPolicyStatus string = 'enabled' + +@description('Optional. The number of days to retain an untagged manifest after which it gets purged.') +param retentionPolicyDays int = 15 + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled.') +param azureADAuthenticationAsArmPolicyStatus string = 'enabled' + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. Soft Delete policy status. Default is disabled.') +param softDeletePolicyStatus string = 'disabled' + +@description('Optional. The number of days after which a soft-deleted item is permanently deleted.') +param softDeletePolicyDays int = 7 + +@description('Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the \'acrSku\' to be \'Premium\'.') +param dataEndpointEnabled bool = false + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the \'acrSku\' to be \'Premium\'.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@allowed([ + 'AzureServices' + 'None' +]) +@description('Optional. Whether to allow trusted Azure services to access a network restricted registry.') +param networkRuleBypassOptions string = 'AzureServices' + +@allowed([ + 'Allow' + 'Deny' +]) +@description('Optional. The default action of allow or deny when no other rules match.') +param networkRuleSetDefaultAction string = 'Deny' + +@description('Optional. The IP ACL rules. Note, requires the \'acrSku\' to be \'Premium\'.') +param networkRuleSetIpRules array = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the \'acrSku\' to be \'Premium\'.') +param privateEndpoints array = [] + +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. Whether or not zone redundancy is enabled for this container registry.') +param zoneRedundancy string = 'Disabled' + +@description('Optional. All replications to create.') +param replications array = [] + +@description('Optional. All webhooks to create.') +param webhooks array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ContainerRegistryRepositoryEvents' + 'ContainerRegistryLoginEvents' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ContainerRegistryRepositoryEvents' + 'ContainerRegistryLoginEvents' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the \'acrSku\' to be \'Premium\'.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption. Note, CMK requires the \'acrSku\' to be \'Premium\'.') +param cMKKeyName string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Conditional. User assigned identity to use when fetching the customer managed key. Note, CMK requires the \'acrSku\' to be \'Premium\'. Required if \'cMKKeyName\' is not empty.') +param cMKUserAssignedIdentityResourceId string = '' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource encryptionIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = if (!empty(cMKUserAssignedIdentityResourceId)) { + name: last(split(cMKUserAssignedIdentityResourceId, '/')) + scope: resourceGroup(split(cMKUserAssignedIdentityResourceId, '/')[2], split(cMKUserAssignedIdentityResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource registry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + identity: identity + tags: tags + sku: { + name: acrSku + } + properties: { + adminUserEnabled: acrAdminUserEnabled + encryption: !empty(cMKKeyName) ? { + status: 'enabled' + keyVaultProperties: { + identity: encryptionIdentity.properties.clientId + keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVaultKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVaultKey.properties.keyUriWithVersion + } + } : null + policies: { + azureADAuthenticationAsArmPolicy: { + status: azureADAuthenticationAsArmPolicyStatus + } + exportPolicy: acrSku == 'Premium' ? { + status: exportPolicyStatus + } : null + quarantinePolicy: { + status: quarantinePolicyStatus + } + trustPolicy: { + type: 'Notary' + status: trustPolicyStatus + } + retentionPolicy: acrSku == 'Premium' ? { + days: retentionPolicyDays + status: retentionPolicyStatus + } : null + softDeletePolicy: { + retentionDays: softDeletePolicyDays + status: softDeletePolicyStatus + } + } + dataEndpointEnabled: dataEndpointEnabled + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkRuleSetIpRules) ? 'Disabled' : null) + networkRuleBypassOptions: networkRuleBypassOptions + networkRuleSet: !empty(networkRuleSetIpRules) ? { + defaultAction: networkRuleSetDefaultAction + ipRules: networkRuleSetIpRules + } : null + zoneRedundancy: acrSku == 'Premium' ? zoneRedundancy : null + } +} + +module registry_replications 'replications/deploy.bicep' = [for (replication, index) in replications: { + name: '${uniqueString(deployment().name, location)}-Registry-Replication-${index}' + params: { + name: replication.name + registryName: registry.name + location: replication.location + regionEndpointEnabled: contains(replication, 'regionEndpointEnabled') ? replication.regionEndpointEnabled : true + zoneRedundancy: contains(replication, 'zoneRedundancy') ? replication.zoneRedundancy : 'Disabled' + tags: contains(replication, 'tags') ? replication.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module registry_webhooks 'webhooks/deploy.bicep' = [for (webhook, index) in webhooks: { + name: '${uniqueString(deployment().name, location)}-Registry-Webhook-${index}' + params: { + name: webhook.name + registryName: registry.name + location: contains(webhook, 'location') ? webhook.location : location + action: contains(webhook, 'action') ? webhook.action : [ + 'chart_delete' + 'chart_push' + 'delete' + 'push' + 'quarantine' + ] + customHeaders: contains(webhook, 'customHeaders') ? webhook.customHeaders : {} + scope: contains(webhook, 'scope') ? webhook.scope : '' + status: contains(webhook, 'status') ? webhook.status : 'enabled' + serviceUri: webhook.serviceUri + tags: contains(webhook, 'tags') ? webhook.tags : {} + } +}] + +resource registry_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${registry.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: registry +} + +resource registry_diagnosticSettingName 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: registry +} + +module registry_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ContainerRegistry-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: registry.id + } +}] + +module registry_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-ContainerRegistry-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(registry.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: registry.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The Name of the Azure container registry.') +output name string = registry.name + +@description('The reference to the Azure container registry.') +output loginServer string = reference(registry.id, '2019-05-01').loginServer + +@description('The name of the Azure container registry.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Azure container registry.') +output resourceId string = registry.id + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(registry.identity, 'principalId') ? registry.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = registry.location diff --git a/modules/Microsoft.ContainerRegistry/registries/readme.md b/modules/Microsoft.ContainerRegistry/registries/readme.md new file mode 100644 index 0000000..785a83e --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/readme.md @@ -0,0 +1,721 @@ +# Container Registries `[Microsoft.ContainerRegistry/registries]` + +Azure Container Registry is a managed, private Docker registry service based on the open-source Docker Registry 2.0. Create and maintain Azure container registries to store and manage your private Docker container images and related artifacts. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ContainerRegistry/registries` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2022-02-01-preview/registries) | +| `Microsoft.ContainerRegistry/registries/replications` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2022-02-01-preview/registries/replications) | +| `Microsoft.ContainerRegistry/registries/webhooks` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2022-02-01-preview/registries/webhooks) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of your Azure container registry. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cMKUserAssignedIdentityResourceId` | string | `''` | User assigned identity to use when fetching the customer managed key. Note, CMK requires the 'acrSku' to be 'Premium'. Required if 'cMKKeyName' is not empty. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `acrAdminUserEnabled` | bool | `False` | | Enable admin user that have push / pull permission to the registry. | +| `acrSku` | string | `'Basic'` | `[Basic, Premium, Standard]` | Tier of your Azure container registry. | +| `azureADAuthenticationAsArmPolicyStatus` | string | `'enabled'` | `[disabled, enabled]` | The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. Note, CMK requires the 'acrSku' to be 'Premium'. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the 'acrSku' to be 'Premium'. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `dataEndpointEnabled` | bool | `False` | | Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ContainerRegistryLoginEvents, ContainerRegistryRepositoryEvents]` | `[ContainerRegistryLoginEvents, ContainerRegistryRepositoryEvents]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exportPolicyStatus` | string | `'disabled'` | `[disabled, enabled]` | The value that indicates whether the export policy is enabled or not. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkRuleBypassOptions` | string | `'AzureServices'` | `[AzureServices, None]` | Whether to allow trusted Azure services to access a network restricted registry. | +| `networkRuleSetDefaultAction` | string | `'Deny'` | `[Allow, Deny]` | The default action of allow or deny when no other rules match. | +| `networkRuleSetIpRules` | array | `[]` | | The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'. | +| `quarantinePolicyStatus` | string | `'disabled'` | `[disabled, enabled]` | The value that indicates whether the quarantine policy is enabled or not. | +| `replications` | _[replications](replications/readme.md)_ array | `[]` | | All replications to create. | +| `retentionPolicyDays` | int | `15` | | The number of days to retain an untagged manifest after which it gets purged. | +| `retentionPolicyStatus` | string | `'enabled'` | `[disabled, enabled]` | The value that indicates whether the retention policy is enabled or not. | +| `roleAssignments` | array | `[]` | | 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'. | +| `softDeletePolicyDays` | int | `7` | | The number of days after which a soft-deleted item is permanently deleted. | +| `softDeletePolicyStatus` | string | `'disabled'` | `[disabled, enabled]` | Soft Delete policy status. Default is disabled. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `trustPolicyStatus` | string | `'disabled'` | `[disabled, enabled]` | The value that indicates whether the trust policy is enabled or not. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `webhooks` | _[webhooks](webhooks/readme.md)_ array | `[]` | | All webhooks to create. | +| `zoneRedundancy` | string | `'Disabled'` | `[Disabled, Enabled]` | Whether or not zone redundancy is enabled for this container registry. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `imageRegistryCredentials` + +The image registry credentials by which the container group is created from. + +

+ +Parameter JSON format + +```json +"acrName": { + "value": { + "server": "acrx001", + } +}, +"acrAdminUserEnabled": { + "value": false +} +``` + +
+ +
+ +Bicep format + +```bicep +acrName: { + server: 'acrx001' +} +acrAdminUserEnabled: false +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `loginServer` | string | The reference to the Azure container registry. | +| `name` | string | The Name of the Azure container registry. | +| `resourceGroupName` | string | The name of the Azure container registry. | +| `resourceId` | string | The resource ID of the Azure container registry. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module registries './Microsoft.ContainerRegistry/registries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Registries' + params: { + // Required parameters + name: '<>azacrencr001' + // Non-required parameters + acrSku: 'Premium' + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + publicNetworkAccess: 'Disabled' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>azacrencr001" + }, + // Non-required parameters + "acrSku": { + "value": "Premium" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module registries './Microsoft.ContainerRegistry/registries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Registries' + params: { + name: '<>azacrmin001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azacrmin001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module registries './Microsoft.ContainerRegistry/registries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Registries' + params: { + // Required parameters + name: '<>azacrx002' + // Non-required parameters + acrAdminUserEnabled: false + acrSku: 'Premium' + azureADAuthenticationAsArmPolicyStatus: 'enabled' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + exportPolicyStatus: 'enabled' + lock: 'CanNotDelete' + networkRuleSetIpRules: [ + { + action: 'Allow' + value: '40.74.28.0/23' + } + ] + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io' + ] + } + service: 'registry' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + quarantinePolicyStatus: 'enabled' + replications: [ + { + location: 'northeurope' + name: 'northeurope' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + softDeletePolicyDays: 7 + softDeletePolicyStatus: 'disabled' + systemAssignedIdentity: true + trustPolicyStatus: 'enabled' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + webhooks: [ + { + name: '<>azacrx001webhook' + serviceUri: 'https://www.contoso.com/webhook' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>azacrx002" + }, + // Non-required parameters + "acrAdminUserEnabled": { + "value": false + }, + "acrSku": { + "value": "Premium" + }, + "azureADAuthenticationAsArmPolicyStatus": { + "value": "enabled" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "exportPolicyStatus": { + "value": "enabled" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkRuleSetIpRules": { + "value": [ + { + "action": "Allow", + "value": "40.74.28.0/23" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io" + ] + }, + "service": "registry", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "quarantinePolicyStatus": { + "value": "enabled" + }, + "replications": { + "value": [ + { + "location": "northeurope", + "name": "northeurope" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "softDeletePolicyDays": { + "value": 7 + }, + "softDeletePolicyStatus": { + "value": "disabled" + }, + "systemAssignedIdentity": { + "value": true + }, + "trustPolicyStatus": { + "value": "enabled" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "webhooks": { + "value": [ + { + "name": "<>azacrx001webhook", + "serviceUri": "https://www.contoso.com/webhook" + } + ] + } + } +} +``` + +
+

+ +

Example 4: Pe

+ +
+ +via Bicep module + +```bicep +module registries './Microsoft.ContainerRegistry/registries/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Registries' + params: { + // Required parameters + name: '<>azacrpe001' + // Non-required parameters + acrSku: 'Premium' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io' + ] + } + service: 'registry' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>azacrpe001" + }, + // Non-required parameters + "acrSku": { + "value": "Premium" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io" + ] + }, + "service": "registry", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ContainerRegistry/registries/replications/deploy.bicep b/modules/Microsoft.ContainerRegistry/registries/replications/deploy.bicep new file mode 100644 index 0000000..90da6e1 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/replications/deploy.bicep @@ -0,0 +1,63 @@ +@description('Conditional. The name of the parent registry. Required if the template is used in a standalone deployment.') +param registryName string + +@description('Required. The name of the replication.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications.') +param regionEndpointEnabled bool = true + +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. Whether or not zone redundancy is enabled for this container registry.') +param zoneRedundancy string = 'Disabled' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource registry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: registryName +} + +resource replication 'Microsoft.ContainerRegistry/registries/replications@2022-02-01-preview' = { + name: name + parent: registry + location: location + tags: tags + properties: { + regionEndpointEnabled: regionEndpointEnabled + zoneRedundancy: zoneRedundancy + } +} + +@description('The name of the replication.') +output name string = replication.name + +@description('The resource ID of the replication.') +output resourceId string = replication.id + +@description('The name of the resource group the replication was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = replication.location diff --git a/modules/Microsoft.ContainerRegistry/registries/replications/readme.md b/modules/Microsoft.ContainerRegistry/registries/replications/readme.md new file mode 100644 index 0000000..425640b --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/replications/readme.md @@ -0,0 +1,92 @@ +# ContainerRegistry Registries Replications `[Microsoft.ContainerRegistry/registries/replications]` + +This module deploys ContainerRegistry Registries Replications. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ContainerRegistry/registries/replications` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2022-02-01-preview/registries/replications) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `registryName` | string | The name of the parent registry. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `regionEndpointEnabled` | bool | `True` | | Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `zoneRedundancy` | string | `'Disabled'` | `[Disabled, Enabled]` | Whether or not zone redundancy is enabled for this container registry. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the replication. | +| `resourceGroupName` | string | The name of the resource group the replication was created in. | +| `resourceId` | string | The resource ID of the replication. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ContainerRegistry/registries/replications/version.json b/modules/Microsoft.ContainerRegistry/registries/replications/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/replications/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ContainerRegistry/registries/version.json b/modules/Microsoft.ContainerRegistry/registries/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.ContainerRegistry/registries/webhooks/deploy.bicep b/modules/Microsoft.ContainerRegistry/registries/webhooks/deploy.bicep new file mode 100644 index 0000000..f58bcf3 --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/webhooks/deploy.bicep @@ -0,0 +1,92 @@ +@description('Conditional. The name of the parent registry. Required if the template is used in a standalone deployment.') +param registryName string + +@description('Optional. The name of the registry webhook.') +@minLength(5) +@maxLength(50) +param name string = '${registryName}webhook' + +@description('Required. The service URI for the webhook to post notifications.') +param serviceUri string + +@allowed([ + 'disabled' + 'enabled' +]) +@description('Optional. The status of the webhook at the time the operation was called.') +param status string = 'enabled' + +@description('Optional. The list of actions that trigger the webhook to post notifications.') +param action array = [ + 'chart_delete' + 'chart_push' + 'delete' + 'push' + 'quarantine' +] + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Custom headers that will be added to the webhook notifications.') +param customHeaders object = {} + +@description('Optional. The scope of repositories where the event can be triggered. For example, \'foo:*\' means events for all tags under repository \'foo\'. \'foo:bar\' means events for \'foo:bar\' only. \'foo\' is equivalent to \'foo:latest\'. Empty means all events.') +param scope string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource registry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: registryName +} + +resource webhook 'Microsoft.ContainerRegistry/registries/webhooks@2022-02-01-preview' = { + name: name + parent: registry + location: location + tags: tags + properties: { + actions: action + customHeaders: customHeaders + scope: scope + serviceUri: serviceUri + status: status + } +} + +@description('The resource ID of the webhook.') +output resourceId string = webhook.id + +@description('The name of the webhook.') +output name string = webhook.name + +@description('The name of the Azure container registry.') +output resourceGroupName string = resourceGroup().name + +@description('The actions of the webhook.') +output actions array = webhook.properties.actions + +@description('The status of the webhook.') +output status string = webhook.properties.status + +@description('The provisioning state of the webhook.') +output provistioningState string = webhook.properties.provisioningState + +@description('The location the resource was deployed into.') +output location string = webhook.location diff --git a/modules/Microsoft.ContainerRegistry/registries/webhooks/readme.md b/modules/Microsoft.ContainerRegistry/registries/webhooks/readme.md new file mode 100644 index 0000000..d009a3e --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/webhooks/readme.md @@ -0,0 +1,98 @@ +# ContainerRegistry Registries Webhooks `[Microsoft.ContainerRegistry/registries/webhooks]` + +This module deploys ContainerRegistry Registries Webhooks. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ContainerRegistry/registries/webhooks` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2022-02-01-preview/registries/webhooks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serviceUri` | string | The service URI for the webhook to post notifications. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `registryName` | string | The name of the parent registry. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `action` | array | `[chart_delete, chart_push, delete, push, quarantine]` | | The list of actions that trigger the webhook to post notifications. | +| `customHeaders` | object | `{object}` | | Custom headers that will be added to the webhook notifications. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `name` | string | `[format('{0}webhook', parameters('registryName'))]` | | The name of the registry webhook. | +| `scope` | string | `''` | | The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events. | +| `status` | string | `'enabled'` | `[disabled, enabled]` | The status of the webhook at the time the operation was called. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `actions` | array | The actions of the webhook. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the webhook. | +| `provistioningState` | string | The provisioning state of the webhook. | +| `resourceGroupName` | string | The name of the Azure container registry. | +| `resourceId` | string | The resource ID of the webhook. | +| `status` | string | The status of the webhook. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ContainerRegistry/registries/webhooks/version.json b/modules/Microsoft.ContainerRegistry/registries/webhooks/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.ContainerRegistry/registries/webhooks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.ContainerService/managedClusters/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ContainerService/managedClusters/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..581a566 --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,77 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'AcrPull': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + 'Azure Kubernetes Service Cluster Admin Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8') + 'Azure Kubernetes Service Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4abbcc35-e782-43d8-92c5-2d3f1bd2253f') + 'Azure Kubernetes Service Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8') + 'Azure Kubernetes Service RBAC Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3498e952-d568-435e-9b2c-8d77e338d7f7') + 'Azure Kubernetes Service RBAC Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + 'Azure Kubernetes Service RBAC Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f6c6a51-bcf8-42ba-9220-52d62157d7db') + 'Azure Kubernetes Service RBAC Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb') + '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') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + '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 managedCluster 'Microsoft.ContainerService/managedClusters@2022-04-02-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(managedCluster.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: managedCluster +}] diff --git a/modules/Microsoft.ContainerService/managedClusters/.test/azure.parameters.json b/modules/Microsoft.ContainerService/managedClusters/.test/azure.parameters.json new file mode 100644 index 0000000..327780a --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/.test/azure.parameters.json @@ -0,0 +1,123 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-aks-azure-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "diskEncryptionSetID": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/diskEncryptionSets/adp-<>-az-des-x-001" + }, + "primaryAgentPoolProfile": { + "value": [ + { + "name": "systempool", + "osDiskSizeGB": 0, + "count": 1, + "enableAutoScaling": true, + "minCount": 1, + "maxCount": 3, + "vmSize": "Standard_DS2_v2", + "osType": "Linux", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "mode": "System", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Primary", + "serviceCidr": "", + "maxPods": 30, + "availabilityZones": [ + "1" + ] + } + ] + }, + "aksClusterNetworkPlugin": { + "value": "azure" + }, + "agentPools": { + "value": [ + { + "name": "userpool1", + "vmSize": "Standard_DS2_v2", + "osDiskSizeGB": 128, + "count": 2, + "osType": "Linux", + "maxCount": 3, + "minCount": 1, + "enableAutoScaling": true, + "scaleSetPriority": "Regular", + "scaleSetEvictionPolicy": "Delete", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "type": "VirtualMachineScaleSets", + "availabilityZones": [ + "1" + ], + "minPods": 2, + "maxPods": 30, + "storageProfile": "ManagedDisks", + "mode": "User", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Secondary" + }, + { + "name": "userpool2", + "vmSize": "Standard_DS2_v2", + "osDiskSizeGB": 128, + "count": 2, + "osType": "Linux", + "maxCount": 3, + "minCount": 1, + "enableAutoScaling": true, + "scaleSetPriority": "Regular", + "scaleSetEvictionPolicy": "Delete", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "type": "VirtualMachineScaleSets", + "availabilityZones": [ + "1" + ], + "minPods": 2, + "maxPods": 30, + "storageProfile": "ManagedDisks", + "mode": "User", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Tertiary" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + } + } +} diff --git a/modules/Microsoft.ContainerService/managedClusters/.test/kubenet.parameters.json b/modules/Microsoft.ContainerService/managedClusters/.test/kubenet.parameters.json new file mode 100644 index 0000000..3e274f4 --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/.test/kubenet.parameters.json @@ -0,0 +1,116 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-aks-kubenet-001" + }, + "primaryAgentPoolProfile": { + "value": [ + { + "name": "systempool", + "osDiskSizeGB": 0, + "count": 1, + "enableAutoScaling": true, + "minCount": 1, + "maxCount": 3, + "vmSize": "Standard_DS2_v2", + "osType": "Linux", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "mode": "System", + "serviceCidr": "", + "maxPods": 30, + "availabilityZones": [ + "1" + ] + } + ] + }, + "aksClusterNetworkPlugin": { + "value": "kubenet" + }, + "agentPools": { + "value": [ + { + "name": "userpool1", + "vmSize": "Standard_DS2_v2", + "osDiskSizeGB": 128, + "count": 2, + "osType": "Linux", + "maxCount": 3, + "minCount": 1, + "enableAutoScaling": true, + "scaleSetPriority": "Regular", + "scaleSetEvictionPolicy": "Delete", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "type": "VirtualMachineScaleSets", + "availabilityZones": [ + "1" + ], + "minPods": 2, + "maxPods": 30, + "storageProfile": "ManagedDisks", + "mode": "User" + }, + { + "name": "userpool2", + "vmSize": "Standard_DS2_v2", + "osDiskSizeGB": 128, + "count": 2, + "osType": "Linux", + "maxCount": 3, + "minCount": 1, + "enableAutoScaling": true, + "scaleSetPriority": "Regular", + "scaleSetEvictionPolicy": "Delete", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "type": "VirtualMachineScaleSets", + "availabilityZones": [ + "1" + ], + "minPods": 2, + "maxPods": 30, + "storageProfile": "ManagedDisks", + "mode": "User" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} diff --git a/modules/Microsoft.ContainerService/managedClusters/agentPools/deploy.bicep b/modules/Microsoft.ContainerService/managedClusters/agentPools/deploy.bicep new file mode 100644 index 0000000..86b7c00 --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/agentPools/deploy.bicep @@ -0,0 +1,242 @@ +@description('Conditional. The name of the parent managed cluster. Required if the template is used in a standalone deployment.') +@minLength(1) +param managedClusterName string + +@description('Required. Name of the agent pool.') +param name string + +@description('Optional. The list of Availability zones to use for nodes. This can only be specified if the AgentPoolType property is "VirtualMachineScaleSets".') +param availabilityZones array = [] + +@description('Optional. Desired Number of agents (VMs) specified to host docker containers. Allowed values must be in the range of 0 to 1000 (inclusive) for user pools and in the range of 1 to 1000 (inclusive) for system pools. The default value is 1.') +@minValue(0) +@maxValue(1000) +param count int = 1 + +@description('Optional. This is the ARM ID of the source object to be used to create the target object.') +param sourceResourceId string = '' + +@description('Optional. Whether to enable auto-scaler.') +@allowed([ + true + false +]) +param enableAutoScaling bool = false + +@description('Optional. This is only supported on certain VM sizes and in certain Azure regions. For more information, see: /azure/aks/enable-host-encryption. For security reasons, this setting should be enabled.') +@allowed([ + true + false +]) +param enableEncryptionAtHost bool = false + +@description('Optional. See Add a FIPS-enabled node pool (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#add-a-fips-enabled-node-pool-preview) for more details.') +@allowed([ + true + false +]) +param enableFIPS bool = false + +@description('Optional. Some scenarios may require nodes in a node pool to receive their own dedicated public IP addresses. A common scenario is for gaming workloads, where a console needs to make a direct connection to a cloud virtual machine to minimize hops. For more information see assigning a public IP per node (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#assign-a-public-ip-per-node-for-your-node-pools). The default is false.') +@allowed([ + true + false +]) +param enableNodePublicIP bool = false + +@description('Optional. Whether to enable UltraSSD.') +@allowed([ + true + false +]) +param enableUltraSSD bool = false + +@description('Optional. GPUInstanceProfile to be used to specify GPU MIG instance profile for supported GPU VM SKU.') +@allowed([ + 'MIG1g' + 'MIG2g' + 'MIG3g' + 'MIG4g' + 'MIG7g' + '' +]) +param gpuInstanceProfile string = '' + +@description('Optional. Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.') +param kubeletDiskType string = '' + +@description('Optional. The maximum number of nodes for auto-scaling.') +param maxCount int = -1 + +@description('Optional. The maximum number of pods that can run on a node.') +param maxPods int = -1 + +@description('Optional. The minimum number of nodes for auto-scaling.') +param minCount int = -1 + +@description('Optional. A cluster must have at least one "System" Agent Pool at all times. For additional information on agent pool restrictions and best practices, see: /azure/aks/use-system-pools.') +param mode string = '' + +@description('Optional. The node labels to be persisted across all nodes in agent pool.') +param nodeLabels object = {} + +@description('Optional. ResourceId of the node PublicIPPrefix.') +param nodePublicIpPrefixId string = '' + +@description('Optional. The taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.') +param nodeTaints array = [] + +@description('Optional. As a best practice, you should upgrade all node pools in an AKS cluster to the same Kubernetes version. The node pool version must have the same major version as the control plane. The node pool minor version must be within two minor versions of the control plane version. The node pool version cannot be greater than the control plane version. For more information see upgrading a node pool (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#upgrade-a-node-pool).') +param orchestratorVersion string = '' + +@description('Optional. OS Disk Size in GB to be used to specify the disk size for every machine in the master/agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified.') +param osDiskSizeGB int = 0 + +@description('Optional. The default is "Ephemeral" if the VM supports it and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to "Managed". May not be changed after creation. For more information see Ephemeral OS (https://docs.microsoft.com/en-us/azure/aks/cluster-configuration#ephemeral-os).') +@allowed([ + 'Ephemeral' + 'Managed' + '' +]) +param osDiskType string = '' + +@description('Optional. Specifies an OS SKU. This value must not be specified if OSType is Windows.') +@allowed([ + 'CBLMariner' + 'Ubuntu' + '' +]) +param osSku string = '' + +@description('Optional. The operating system type. The default is Linux.') +@allowed([ + 'Linux' + 'Windows' +]) +param osType string = 'Linux' + +@description('Optional. Subnet ID for the pod IPs. If omitted, pod IPs are statically assigned on the node subnet (see vnetSubnetID for more details). This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}.') +param podSubnetId string = '' + +@description('Optional. The ID for the Proximity Placement Group.') +param proximityPlacementGroupId string = '' + +@description('Optional. Describes how VMs are added to or removed from Agent Pools. See billing states (https://docs.microsoft.com/en-us/azure/virtual-machines/states-billing).') +@allowed([ + 'Deallocate' + 'Delete' +]) +param scaleDownMode string = 'Delete' + +@description('Optional. The eviction policy specifies what to do with the VM when it is evicted. The default is Delete. For more information about eviction see spot VMs.') +@allowed([ + 'Deallocate' + 'Delete' +]) +param scaleSetEvictionPolicy string = 'Delete' + +@description('Optional. The Virtual Machine Scale Set priority.') +@allowed([ + 'Regular' + 'Spot' + '' +]) +param scaleSetPriority string = '' + +@description('Optional. Possible values are any decimal value greater than zero or -1 which indicates the willingness to pay any on-demand price. For more details on spot pricing, see spot VMs pricing (https://docs.microsoft.com/en-us/azure/virtual-machines/spot-vms#pricing).') +param spotMaxPrice int = -1 + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The type of Agent Pool.') +param type string = '' + +@description('Optional. This can either be set to an integer (e.g. "5") or a percentage (e.g. "50%"). If a percentage is specified, it is the percentage of the total agent pool size at the time of the upgrade. For percentages, fractional nodes are rounded up. If not specified, the default is 1. For more information, including best practices, see: /azure/aks/upgrade-cluster#customize-node-surge-upgrade.') +param maxSurge string = '' + +@description('Optional. VM size. VM size availability varies by region. If a node contains insufficient compute resources (memory, cpu, etc) pods might fail to run correctly. For more details on restricted VM sizes, see: /azure/aks/quotas-skus-regions.') +param vmSize string = 'Standard_D2s_v3' + +@description('Optional. Node Subnet ID. If this is not specified, a VNET and subnet will be generated and used. If no podSubnetID is specified, this applies to nodes and pods, otherwise it applies to just nodes. This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}.') +param vnetSubnetId string = '' + +@description('Optional. Determines the type of workload a node can run.') +param workloadRuntime string = '' + +var creationData = { + sourceResourceId: !empty(sourceResourceId) ? sourceResourceId : null +} + +var upgradeSettings = { + maxSurge: maxSurge +} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedCluster 'Microsoft.ContainerService/managedClusters@2022-06-01' existing = { + name: managedClusterName +} + +resource agentPool 'Microsoft.ContainerService/managedClusters/agentPools@2022-06-01' = { + name: name + parent: managedCluster + properties: { + availabilityZones: availabilityZones + count: count + creationData: !empty(sourceResourceId) ? creationData : null + enableAutoScaling: enableAutoScaling + enableEncryptionAtHost: enableEncryptionAtHost + enableFIPS: enableFIPS + enableNodePublicIP: enableNodePublicIP + enableUltraSSD: enableUltraSSD + gpuInstanceProfile: !empty(gpuInstanceProfile) ? any(gpuInstanceProfile) : null + kubeletDiskType: kubeletDiskType + maxCount: maxCount != -1 ? maxCount : null + maxPods: maxPods != -1 ? maxPods : null + minCount: minCount != -1 ? minCount : null + mode: !empty(mode) ? mode : null + nodeLabels: nodeLabels + nodePublicIPPrefixID: !empty(nodePublicIpPrefixId) ? nodePublicIpPrefixId : null + nodeTaints: nodeTaints + orchestratorVersion: orchestratorVersion + osDiskSizeGB: osDiskSizeGB != -1 ? osDiskSizeGB : null + osDiskType: !empty(osDiskType) ? any(osDiskType) : null + osSKU: !empty(osSku) ? any(osSku) : null + osType: osType + podSubnetID: !empty(podSubnetId) ? podSubnetId : null + proximityPlacementGroupID: !empty(proximityPlacementGroupId) ? proximityPlacementGroupId : null + scaleDownMode: scaleDownMode + scaleSetEvictionPolicy: scaleSetEvictionPolicy + scaleSetPriority: !empty(scaleSetPriority) ? any(scaleSetPriority) : null + spotMaxPrice: spotMaxPrice + tags: tags + type: type + upgradeSettings: upgradeSettings + vmSize: vmSize + vnetSubnetID: vnetSubnetId + workloadRuntime: workloadRuntime + } +} + +@description('The name of the agent pool.') +output name string = agentPool.name + +@description('The resource ID of the agent pool.') +output resourceId string = agentPool.id + +@description('The resource group the agent pool was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ContainerService/managedClusters/agentPools/readme.md b/modules/Microsoft.ContainerService/managedClusters/agentPools/readme.md new file mode 100644 index 0000000..e3961dd --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/agentPools/readme.md @@ -0,0 +1,121 @@ +# Managed Cluster AgentPool `[Microsoft.ContainerService/managedClusters/agentPools]` + +This module deploys an Agent Pool for a Container Service Managed Cluster + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ContainerService/managedClusters/agentPools` | [2022-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2022-06-01/managedClusters/agentPools) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the agent pool. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedClusterName` | string | The name of the parent managed cluster. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `availabilityZones` | array | `[]` | | The list of Availability zones to use for nodes. This can only be specified if the AgentPoolType property is "VirtualMachineScaleSets". | +| `count` | int | `1` | | Desired Number of agents (VMs) specified to host docker containers. Allowed values must be in the range of 0 to 1000 (inclusive) for user pools and in the range of 1 to 1000 (inclusive) for system pools. The default value is 1. | +| `enableAutoScaling` | bool | `False` | `[False, True]` | Whether to enable auto-scaler. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableEncryptionAtHost` | bool | `False` | `[False, True]` | This is only supported on certain VM sizes and in certain Azure regions. For more information, see: /azure/aks/enable-host-encryption. For security reasons, this setting should be enabled. | +| `enableFIPS` | bool | `False` | `[False, True]` | See Add a FIPS-enabled node pool (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#add-a-fips-enabled-node-pool-preview) for more details. | +| `enableNodePublicIP` | bool | `False` | `[False, True]` | Some scenarios may require nodes in a node pool to receive their own dedicated public IP addresses. A common scenario is for gaming workloads, where a console needs to make a direct connection to a cloud virtual machine to minimize hops. For more information see assigning a public IP per node (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#assign-a-public-ip-per-node-for-your-node-pools). The default is false. | +| `enableUltraSSD` | bool | `False` | `[False, True]` | Whether to enable UltraSSD. | +| `gpuInstanceProfile` | string | `''` | `['', MIG1g, MIG2g, MIG3g, MIG4g, MIG7g]` | GPUInstanceProfile to be used to specify GPU MIG instance profile for supported GPU VM SKU. | +| `kubeletDiskType` | string | `''` | | Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage. | +| `maxCount` | int | `-1` | | The maximum number of nodes for auto-scaling. | +| `maxPods` | int | `-1` | | The maximum number of pods that can run on a node. | +| `maxSurge` | string | `''` | | This can either be set to an integer (e.g. "5") or a percentage (e.g. "50%"). If a percentage is specified, it is the percentage of the total agent pool size at the time of the upgrade. For percentages, fractional nodes are rounded up. If not specified, the default is 1. For more information, including best practices, see: /azure/aks/upgrade-cluster#customize-node-surge-upgrade. | +| `minCount` | int | `-1` | | The minimum number of nodes for auto-scaling. | +| `mode` | string | `''` | | A cluster must have at least one "System" Agent Pool at all times. For additional information on agent pool restrictions and best practices, see: /azure/aks/use-system-pools. | +| `nodeLabels` | object | `{object}` | | The node labels to be persisted across all nodes in agent pool. | +| `nodePublicIpPrefixId` | string | `''` | | ResourceId of the node PublicIPPrefix. | +| `nodeTaints` | array | `[]` | | The taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule. | +| `orchestratorVersion` | string | `''` | | As a best practice, you should upgrade all node pools in an AKS cluster to the same Kubernetes version. The node pool version must have the same major version as the control plane. The node pool minor version must be within two minor versions of the control plane version. The node pool version cannot be greater than the control plane version. For more information see upgrading a node pool (https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools#upgrade-a-node-pool). | +| `osDiskSizeGB` | int | `0` | | OS Disk Size in GB to be used to specify the disk size for every machine in the master/agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified. | +| `osDiskType` | string | `''` | `['', Ephemeral, Managed]` | The default is "Ephemeral" if the VM supports it and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to "Managed". May not be changed after creation. For more information see Ephemeral OS (https://docs.microsoft.com/en-us/azure/aks/cluster-configuration#ephemeral-os). | +| `osSku` | string | `''` | `['', CBLMariner, Ubuntu]` | Specifies an OS SKU. This value must not be specified if OSType is Windows. | +| `osType` | string | `'Linux'` | `[Linux, Windows]` | The operating system type. The default is Linux. | +| `podSubnetId` | string | `''` | | Subnet ID for the pod IPs. If omitted, pod IPs are statically assigned on the node subnet (see vnetSubnetID for more details). This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}. | +| `proximityPlacementGroupId` | string | `''` | | The ID for the Proximity Placement Group. | +| `scaleDownMode` | string | `'Delete'` | `[Deallocate, Delete]` | Describes how VMs are added to or removed from Agent Pools. See billing states (https://docs.microsoft.com/en-us/azure/virtual-machines/states-billing). | +| `scaleSetEvictionPolicy` | string | `'Delete'` | `[Deallocate, Delete]` | The eviction policy specifies what to do with the VM when it is evicted. The default is Delete. For more information about eviction see spot VMs. | +| `scaleSetPriority` | string | `''` | `['', Regular, Spot]` | The Virtual Machine Scale Set priority. | +| `sourceResourceId` | string | `''` | | This is the ARM ID of the source object to be used to create the target object. | +| `spotMaxPrice` | int | `-1` | | Possible values are any decimal value greater than zero or -1 which indicates the willingness to pay any on-demand price. For more details on spot pricing, see spot VMs pricing (https://docs.microsoft.com/en-us/azure/virtual-machines/spot-vms#pricing). | +| `tags` | object | `{object}` | | Tags of the resource. | +| `type` | string | `''` | | The type of Agent Pool. | +| `vmSize` | string | `'Standard_D2s_v3'` | | VM size. VM size availability varies by region. If a node contains insufficient compute resources (memory, cpu, etc) pods might fail to run correctly. For more details on restricted VM sizes, see: /azure/aks/quotas-skus-regions. | +| `vnetSubnetId` | string | `''` | | Node Subnet ID. If this is not specified, a VNET and subnet will be generated and used. If no podSubnetID is specified, this applies to nodes and pods, otherwise it applies to just nodes. This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}. | +| `workloadRuntime` | string | `''` | | Determines the type of workload a node can run. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the agent pool. | +| `resourceGroupName` | string | The resource group the agent pool was deployed into. | +| `resourceId` | string | The resource ID of the agent pool. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ContainerService/managedClusters/agentPools/version.json b/modules/Microsoft.ContainerService/managedClusters/agentPools/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/agentPools/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ContainerService/managedClusters/deploy.bicep b/modules/Microsoft.ContainerService/managedClusters/deploy.bicep new file mode 100644 index 0000000..f4e474c --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/deploy.bicep @@ -0,0 +1,607 @@ +@description('Required. Specifies the name of the AKS cluster.') +param name string + +@description('Optional. Specifies the location of AKS cluster. It picks up Resource Group\'s location by default.') +param location string = resourceGroup().location + +@description('Optional. Specifies the DNS prefix specified when creating the managed cluster.') +param aksClusterDnsPrefix string = name + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Specifies the network plugin used for building Kubernetes network. - azure or kubenet.') +@allowed([ + '' + 'azure' + 'kubenet' +]) +param aksClusterNetworkPlugin string = '' + +@description('Optional. Specifies the network policy used for building Kubernetes network. - calico or azure.') +@allowed([ + '' + 'azure' + 'calico' +]) +param aksClusterNetworkPolicy string = '' + +@description('Optional. Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used.') +param aksClusterPodCidr string = '' + +@description('Optional. A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges.') +param aksClusterServiceCidr string = '' + +@description('Optional. Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr.') +param aksClusterDnsServiceIP string = '' + +@description('Optional. Specifies the CIDR notation IP range assigned to the Docker bridge network. It must not overlap with any Subnet IP ranges or the Kubernetes service address range.') +param aksClusterDockerBridgeCidr string = '' + +@description('Optional. Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools.') +@allowed([ + 'basic' + 'standard' +]) +param aksClusterLoadBalancerSku string = 'standard' + +@description('Optional. Outbound IP Count for the Load balancer.') +param managedOutboundIPCount int = 0 + +@description('Optional. Specifies outbound (egress) routing method. - loadBalancer or userDefinedRouting.') +@allowed([ + 'loadBalancer' + 'userDefinedRouting' +]) +param aksClusterOutboundType string = 'loadBalancer' + +@description('Optional. Tier of a managed cluster SKU. - Free or Paid.') +@allowed([ + 'Free' + 'Paid' +]) +param aksClusterSkuTier string = 'Free' + +@description('Optional. Version of Kubernetes specified when creating the managed cluster.') +param aksClusterKubernetesVersion string = '' + +@description('Optional. Specifies the administrator username of Linux virtual machines.') +param aksClusterAdminUsername string = 'azureuser' + +@description('Optional. Specifies the SSH RSA public key string for the Linux nodes.') +param aksClusterSshPublicKey string = '' + +@description('Optional. Information about a service principal identity for the cluster to use for manipulating Azure APIs.') +param aksServicePrincipalProfile object = {} + +@description('Optional. The client AAD application ID.') +param aadProfileClientAppID string = '' + +@description('Optional. The server AAD application ID.') +param aadProfileServerAppID string = '' + +@description('Optional. The server AAD application secret.') +#disable-next-line secure-secrets-in-params // Not a secret +param aadProfileServerAppSecret string = '' + +@description('Optional. Specifies the tenant ID of the Azure Active Directory used by the AKS cluster for authentication.') +param aadProfileTenantId string = subscription().tenantId + +@description('Optional. Specifies the AAD group object IDs that will have admin role of the cluster.') +param aadProfileAdminGroupObjectIDs array = [] + +@description('Optional. Specifies whether to enable managed AAD integration.') +param aadProfileManaged bool = true + +@description('Optional. Whether to enable Kubernetes Role-Based Access Control.') +param enableRBAC bool = true + +@description('Optional. Specifies whether to enable Azure RBAC for Kubernetes authorization.') +param aadProfileEnableAzureRBAC bool = enableRBAC + +@description('Optional. If set to true, getting static credentials will be disabled for this cluster. This must only be used on Managed Clusters that are AAD enabled.') +param disableLocalAccounts bool = false + +@description('Optional. Name of the resource group containing agent pool nodes.') +param nodeResourceGroup string = '${resourceGroup().name}_aks_${name}_nodes' + +@description('Optional. IP ranges are specified in CIDR format, e.g. 137.117.106.88/29. This feature is not compatible with clusters that use Public IP Per Node, or clusters that are using a Basic Load Balancer.') +param authorizedIPRanges array = [] + +@description('Optional. Whether to disable run command for the cluster or not.') +param disableRunCommand bool = false + +@description('Optional. Specifies whether to create the cluster as a private cluster or not.') +param enablePrivateCluster bool = false + +@description('Optional. Whether to create additional public FQDN for private cluster or not.') +param enablePrivateClusterPublicFQDN bool = false + +@description('Optional. If AKS will create a Private DNS Zone in the Node Resource Group.') +param usePrivateDNSZone bool = false + +@description('Required. Properties of the primary agent pool.') +param primaryAgentPoolProfile array + +@description('Optional. Define one or more secondary/additional agent pools.') +param agentPools array = [] + +@description('Optional. Specifies whether the httpApplicationRouting add-on is enabled or not.') +param httpApplicationRoutingEnabled bool = false + +@description('Optional. Specifies whether the ingressApplicationGateway (AGIC) add-on is enabled or not.') +param ingressApplicationGatewayEnabled bool = false + +@description('Conditional. Specifies the resource ID of connected application gateway. Required if `ingressApplicationGatewayEnabled` is set to `true`.') +param appGatewayResourceId string = '' + +@description('Optional. Specifies whether the aciConnectorLinux add-on is enabled or not.') +param aciConnectorLinuxEnabled bool = false + +@description('Optional. Specifies whether the azurepolicy add-on is enabled or not. For security reasons, this setting should be enabled.') +param azurePolicyEnabled bool = true + +@description('Optional. Specifies the azure policy version to use.') +param azurePolicyVersion string = 'v2' + +@description('Optional. Specifies whether the kubeDashboard add-on is enabled or not.') +param kubeDashboardEnabled bool = false + +@description('Optional. Specifies whether the KeyvaultSecretsProvider add-on is enabled or not.') +#disable-next-line secure-secrets-in-params // Not a secret +param enableKeyvaultSecretsProvider bool = false + +@allowed([ + 'false' + 'true' +]) +@description('Optional. Specifies whether the KeyvaultSecretsProvider add-on uses secret rotation.') +#disable-next-line secure-secrets-in-params // Not a secret +param enableSecretRotation string = 'false' + +@description('Optional. Specifies the scan interval of the auto-scaler of the AKS cluster.') +param autoScalerProfileScanInterval string = '10s' + +@description('Optional. Specifies the scale down delay after add of the auto-scaler of the AKS cluster.') +param autoScalerProfileScaleDownDelayAfterAdd string = '10m' + +@description('Optional. Specifies the scale down delay after delete of the auto-scaler of the AKS cluster.') +param autoScalerProfileScaleDownDelayAfterDelete string = '20s' + +@description('Optional. Specifies scale down delay after failure of the auto-scaler of the AKS cluster.') +param autoScalerProfileScaleDownDelayAfterFailure string = '3m' + +@description('Optional. Specifies the scale down unneeded time of the auto-scaler of the AKS cluster.') +param autoScalerProfileScaleDownUnneededTime string = '10m' + +@description('Optional. Specifies the scale down unready time of the auto-scaler of the AKS cluster.') +param autoScalerProfileScaleDownUnreadyTime string = '20m' + +@description('Optional. Specifies the utilization threshold of the auto-scaler of the AKS cluster.') +param autoScalerProfileUtilizationThreshold string = '0.5' + +@description('Optional. Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster.') +param autoScalerProfileMaxGracefulTerminationSec string = '600' + +@allowed([ + 'false' + 'true' +]) +@description('Optional. Specifies the balance of similar node groups for the auto-scaler of the AKS cluster.') +param autoScalerProfileBalanceSimilarNodeGroups string = 'false' + +@allowed([ + 'least-waste' + 'most-pods' + 'priority' + 'random' +]) +@description('Optional. Specifies the expand strategy for the auto-scaler of the AKS cluster.') +param autoScalerProfileExpander string = 'random' + +@description('Optional. Specifies the maximum empty bulk delete for the auto-scaler of the AKS cluster.') +param autoScalerProfileMaxEmptyBulkDelete string = '10' + +@description('Optional. Specifies the maximum node provisioning time for the auto-scaler of the AKS cluster. Values must be an integer followed by an "m". No unit of time other than minutes (m) is supported.') +param autoScalerProfileMaxNodeProvisionTime string = '15m' + +@description('Optional. Specifies the mximum total unready percentage for the auto-scaler of the AKS cluster. The maximum is 100 and the minimum is 0.') +param autoScalerProfileMaxTotalUnreadyPercentage string = '45' + +@description('Optional. For scenarios like burst/batch scale where you do not want CA to act before the kubernetes scheduler could schedule all the pods, you can tell CA to ignore unscheduled pods before they are a certain age. Values must be an integer followed by a unit ("s" for seconds, "m" for minutes, "h" for hours, etc).') +param autoScalerProfileNewPodScaleUpDelay string = '0s' + +@description('Optional. Specifies the OK total unready count for the auto-scaler of the AKS cluster.') +param autoScalerProfileOkTotalUnreadyCount string = '3' + +@allowed([ + 'false' + 'true' +]) +@description('Optional. Specifies if nodes with local storage should be skipped for the auto-scaler of the AKS cluster.') +param autoScalerProfileSkipNodesWithLocalStorage string = 'true' + +@allowed([ + 'false' + 'true' +]) +@description('Optional. Specifies if nodes with system pods should be skipped for the auto-scaler of the AKS cluster.') +param autoScalerProfileSkipNodesWithSystemPods string = 'true' + +@description('Optional. Running in Kubenet is disabled by default due to the security related nature of AAD Pod Identity and the risks of IP spoofing.') +param podIdentityProfileAllowNetworkPluginKubenet bool = false + +@description('Optional. Whether the pod identity addon is enabled.') +param podIdentityProfileEnable bool = false + +@description('Optional. The pod identities to use in the cluster.') +param podIdentityProfileUserAssignedIdentities array = [] + +@description('Optional. The pod identity exceptions to allow.') +param podIdentityProfileUserAssignedIdentityExceptions array = [] + +@description('Optional. Whether the The OIDC issuer profile of the Managed Cluster is enabled.') +param enableOidcIssuerProfile bool = false + +@description('Optional. Whether to enable Azure Defender.') +param enableAzureDefender bool = false + +@description('Optional. Whether to enable Kubernetes pod security policy.') +param enablePodSecurityPolicy bool = false + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Specifies whether the OMS agent is enabled.') +param omsAgentEnabled bool = true + +@description('Optional. Resource ID of the monitoring log analytics workspace.') +param monitoringWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The resource ID of the disc encryption set to apply to the clsuter. For security reasons, this value should be provided.') +param diskEncryptionSetID string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'kube-apiserver' + 'kube-audit' + 'kube-controller-manager' + 'kube-scheduler' + 'cluster-autoscaler' + 'kube-audit-admin' + 'guard' +]) +param diagnosticLogCategoriesToEnable array = [ + 'kube-apiserver' + 'kube-audit' + 'kube-controller-manager' + 'kube-scheduler' + 'cluster-autoscaler' + 'kube-audit-admin' + 'guard' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +var aksClusterLinuxProfile = { + adminUsername: aksClusterAdminUsername + ssh: { + publicKeys: [ + { + keyData: aksClusterSshPublicKey + } + ] + } +} + +var lbProfile = { + managedOutboundIPs: { + count: managedOutboundIPCount + } + effectiveOutboundIPs: [] +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedCluster 'Microsoft.ContainerService/managedClusters@2022-06-01' = { + name: name + location: location + tags: tags + identity: identity + sku: { + name: 'Basic' + tier: aksClusterSkuTier + } + properties: { + diskEncryptionSetID: !empty(diskEncryptionSetID) ? diskEncryptionSetID : null + kubernetesVersion: (empty(aksClusterKubernetesVersion) ? null : aksClusterKubernetesVersion) + dnsPrefix: aksClusterDnsPrefix + agentPoolProfiles: primaryAgentPoolProfile + linuxProfile: (empty(aksClusterSshPublicKey) ? null : aksClusterLinuxProfile) + servicePrincipalProfile: (empty(aksServicePrincipalProfile) ? null : aksServicePrincipalProfile) + addonProfiles: { + httpApplicationRouting: { + enabled: httpApplicationRoutingEnabled + } + ingressApplicationGateway: { + enabled: ingressApplicationGatewayEnabled && !empty(appGatewayResourceId) + config: { + applicationGatewayId: !empty(appGatewayResourceId) ? any(appGatewayResourceId) : null + effectiveApplicationGatewayId: !empty(appGatewayResourceId) ? any(appGatewayResourceId) : null + } + } + omsagent: { + enabled: omsAgentEnabled && !empty(monitoringWorkspaceId) + config: { + logAnalyticsWorkspaceResourceID: !empty(monitoringWorkspaceId) ? any(monitoringWorkspaceId) : null + } + } + aciConnectorLinux: { + enabled: aciConnectorLinuxEnabled + } + azurepolicy: { + enabled: azurePolicyEnabled + config: { + version: azurePolicyVersion + } + } + kubeDashboard: { + enabled: kubeDashboardEnabled + } + azureKeyvaultSecretsProvider: { + enabled: enableKeyvaultSecretsProvider + config: { + enableSecretRotation: enableSecretRotation + } + } + } + oidcIssuerProfile: enableOidcIssuerProfile ? { + enabled: enableOidcIssuerProfile + } : null + enableRBAC: enableRBAC + disableLocalAccounts: disableLocalAccounts + nodeResourceGroup: nodeResourceGroup + enablePodSecurityPolicy: enablePodSecurityPolicy + networkProfile: { + networkPlugin: !empty(aksClusterNetworkPlugin) ? any(aksClusterNetworkPlugin) : null + networkPolicy: !empty(aksClusterNetworkPolicy) ? any(aksClusterNetworkPolicy) : null + podCidr: !empty(aksClusterPodCidr) ? aksClusterPodCidr : null + serviceCidr: !empty(aksClusterServiceCidr) ? aksClusterServiceCidr : null + dnsServiceIP: !empty(aksClusterDnsServiceIP) ? aksClusterDnsServiceIP : null + dockerBridgeCidr: !empty(aksClusterDockerBridgeCidr) ? aksClusterDockerBridgeCidr : null + outboundType: aksClusterOutboundType + loadBalancerSku: aksClusterLoadBalancerSku + loadBalancerProfile: managedOutboundIPCount != 0 ? lbProfile : null + } + aadProfile: { + clientAppID: aadProfileClientAppID + serverAppID: aadProfileServerAppID + serverAppSecret: aadProfileServerAppSecret + managed: aadProfileManaged + enableAzureRBAC: aadProfileEnableAzureRBAC + adminGroupObjectIDs: aadProfileAdminGroupObjectIDs + tenantID: aadProfileTenantId + } + autoScalerProfile: { + 'balance-similar-node-groups': autoScalerProfileBalanceSimilarNodeGroups + 'expander': autoScalerProfileExpander + 'max-empty-bulk-delete': autoScalerProfileMaxEmptyBulkDelete + 'max-graceful-termination-sec': autoScalerProfileMaxGracefulTerminationSec + 'max-node-provision-time': autoScalerProfileMaxNodeProvisionTime + 'max-total-unready-percentage': autoScalerProfileMaxTotalUnreadyPercentage + 'new-pod-scale-up-delay': autoScalerProfileNewPodScaleUpDelay + 'ok-total-unready-count': autoScalerProfileOkTotalUnreadyCount + 'scale-down-delay-after-add': autoScalerProfileScaleDownDelayAfterAdd + 'scale-down-delay-after-delete': autoScalerProfileScaleDownDelayAfterDelete + 'scale-down-delay-after-failure': autoScalerProfileScaleDownDelayAfterFailure + 'scale-down-unneeded-time': autoScalerProfileScaleDownUnneededTime + 'scale-down-unready-time': autoScalerProfileScaleDownUnreadyTime + 'scale-down-utilization-threshold': autoScalerProfileUtilizationThreshold + 'scan-interval': autoScalerProfileScanInterval + 'skip-nodes-with-local-storage': autoScalerProfileSkipNodesWithLocalStorage + 'skip-nodes-with-system-pods': autoScalerProfileSkipNodesWithSystemPods + } + apiServerAccessProfile: { + authorizedIPRanges: authorizedIPRanges + disableRunCommand: disableRunCommand + enablePrivateCluster: enablePrivateCluster + enablePrivateClusterPublicFQDN: enablePrivateClusterPublicFQDN + privateDNSZone: usePrivateDNSZone ? 'system' : '' + } + podIdentityProfile: { + allowNetworkPluginKubenet: podIdentityProfileAllowNetworkPluginKubenet + enabled: podIdentityProfileEnable + userAssignedIdentities: podIdentityProfileUserAssignedIdentities + userAssignedIdentityExceptions: podIdentityProfileUserAssignedIdentityExceptions + } + securityProfile: enableAzureDefender ? { + azureDefender: { + enabled: enableAzureDefender + logAnalyticsWorkspaceResourceId: !empty(monitoringWorkspaceId) ? monitoringWorkspaceId : null + } + } : null + } +} + +module managedCluster_agentPools 'agentPools/deploy.bicep' = [for (agentPool, index) in agentPools: { + name: '${uniqueString(deployment().name, location)}-ManagedCluster-AgentPool-${index}' + params: { + managedClusterName: managedCluster.name + name: agentPool.name + availabilityZones: contains(agentPool, 'availabilityZones') ? agentPool.availabilityZones : [] + count: contains(agentPool, 'count') ? agentPool.count : 1 + sourceResourceId: contains(agentPool, 'sourceResourceId') ? agentPool.sourceResourceId : '' + enableAutoScaling: contains(agentPool, 'enableAutoScaling') ? agentPool.enableAutoScaling : false + enableEncryptionAtHost: contains(agentPool, 'enableEncryptionAtHost') ? agentPool.enableEncryptionAtHost : false + enableFIPS: contains(agentPool, 'enableFIPS') ? agentPool.enableFIPS : false + enableNodePublicIP: contains(agentPool, 'enableNodePublicIP') ? agentPool.enableNodePublicIP : false + enableUltraSSD: contains(agentPool, 'enableUltraSSD') ? agentPool.enableUltraSSD : false + gpuInstanceProfile: contains(agentPool, 'gpuInstanceProfile') ? agentPool.gpuInstanceProfile : '' + kubeletDiskType: contains(agentPool, 'kubeletDiskType') ? agentPool.kubeletDiskType : '' + maxCount: contains(agentPool, 'maxCount') ? agentPool.maxCount : -1 + maxPods: contains(agentPool, 'maxPods') ? agentPool.maxPods : -1 + minCount: contains(agentPool, 'minCount') ? agentPool.minCount : -1 + mode: contains(agentPool, 'mode') ? agentPool.mode : '' + nodeLabels: contains(agentPool, 'nodeLabels') ? agentPool.nodeLabels : {} + nodePublicIpPrefixId: contains(agentPool, 'nodePublicIpPrefixId') ? agentPool.nodePublicIpPrefixId : '' + nodeTaints: contains(agentPool, 'nodeTaints') ? agentPool.nodeTaints : [] + orchestratorVersion: contains(agentPool, 'orchestratorVersion') ? agentPool.orchestratorVersion : '' + osDiskSizeGB: contains(agentPool, 'osDiskSizeGB') ? agentPool.osDiskSizeGB : -1 + osDiskType: contains(agentPool, 'osDiskType') ? agentPool.osDiskType : '' + osSku: contains(agentPool, 'osSku') ? agentPool.osSku : '' + osType: contains(agentPool, 'osType') ? agentPool.osType : 'Linux' + podSubnetId: contains(agentPool, 'podSubnetId') ? agentPool.podSubnetId : '' + proximityPlacementGroupId: contains(agentPool, 'proximityPlacementGroupId') ? agentPool.proximityPlacementGroupId : '' + scaleDownMode: contains(agentPool, 'scaleDownMode') ? agentPool.scaleDownMode : 'Delete' + scaleSetEvictionPolicy: contains(agentPool, 'scaleSetEvictionPolicy') ? agentPool.scaleSetEvictionPolicy : 'Delete' + scaleSetPriority: contains(agentPool, 'scaleSetPriority') ? agentPool.scaleSetPriority : '' + spotMaxPrice: contains(agentPool, 'spotMaxPrice') ? agentPool.spotMaxPrice : -1 + tags: contains(agentPool, 'tags') ? agentPool.tags : {} + type: contains(agentPool, 'type') ? agentPool.type : '' + maxSurge: contains(agentPool, 'maxSurge') ? agentPool.maxSurge : '' + vmSize: contains(agentPool, 'vmSize') ? agentPool.vmSize : 'Standard_D2s_v3' + vnetSubnetId: contains(agentPool, 'vnetSubnetId') ? agentPool.vnetSubnetId : '' + workloadRuntime: contains(agentPool, 'workloadRuntime') ? agentPool.workloadRuntime : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource managedCluster_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${managedCluster.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: managedCluster +} + +resource managedCluster_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: managedCluster +} + +module managedCluster_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ManagedCluster-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: managedCluster.id + } +}] + +@description('The resource ID of the managed cluster.') +output resourceId string = managedCluster.id + +@description('The resource group the managed cluster was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the managed cluster.') +output name string = managedCluster.name + +@description('The control plane FQDN of the managed cluster.') +output controlPlaneFQDN string = enablePrivateCluster ? managedCluster.properties.privateFQDN : managedCluster.properties.fqdn + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(managedCluster.identity, 'principalId') ? managedCluster.identity.principalId : '' + +@description('The Object ID of the AKS identity.') +output kubeletidentityObjectId string = contains(managedCluster.properties, 'identityProfile') ? contains(managedCluster.properties.identityProfile, 'kubeletidentity') ? managedCluster.properties.identityProfile.kubeletidentity.objectId : '' : '' + +@description('The Object ID of the OMS agent identity.') +output omsagentIdentityObjectId string = contains(managedCluster.properties, 'addonProfiles') ? contains(managedCluster.properties.addonProfiles, 'omsagent') ? contains(managedCluster.properties.addonProfiles.omsagent, 'identity') ? managedCluster.properties.addonProfiles.omsagent.identity.objectId : '' : '' : '' + +@description('The location the resource was deployed into.') +output location string = managedCluster.location diff --git a/modules/Microsoft.ContainerService/managedClusters/readme.md b/modules/Microsoft.ContainerService/managedClusters/readme.md new file mode 100644 index 0000000..a74b4b9 --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/readme.md @@ -0,0 +1,851 @@ +# Azure Kubernetes Services `[Microsoft.ContainerService/managedClusters]` + +This module deploys Azure Kubernetes Cluster (AKS). + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ContainerService/managedClusters` | [2022-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2022-06-01/managedClusters) | +| `Microsoft.ContainerService/managedClusters/agentPools` | [2022-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2022-06-01/managedClusters/agentPools) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the AKS cluster. | +| `primaryAgentPoolProfile` | array | Properties of the primary agent pool. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `appGatewayResourceId` | string | `''` | Specifies the resource ID of connected application gateway. Required if `ingressApplicationGatewayEnabled` is set to `true`. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `aadProfileAdminGroupObjectIDs` | array | `[]` | | Specifies the AAD group object IDs that will have admin role of the cluster. | +| `aadProfileClientAppID` | string | `''` | | The client AAD application ID. | +| `aadProfileEnableAzureRBAC` | bool | `[parameters('enableRBAC')]` | | Specifies whether to enable Azure RBAC for Kubernetes authorization. | +| `aadProfileManaged` | bool | `True` | | Specifies whether to enable managed AAD integration. | +| `aadProfileServerAppID` | string | `''` | | The server AAD application ID. | +| `aadProfileServerAppSecret` | string | `''` | | The server AAD application secret. | +| `aadProfileTenantId` | string | `[subscription().tenantId]` | | Specifies the tenant ID of the Azure Active Directory used by the AKS cluster for authentication. | +| `aciConnectorLinuxEnabled` | bool | `False` | | Specifies whether the aciConnectorLinux add-on is enabled or not. | +| `agentPools` | _[agentPools](agentPools/readme.md)_ array | `[]` | | Define one or more secondary/additional agent pools. | +| `aksClusterAdminUsername` | string | `'azureuser'` | | Specifies the administrator username of Linux virtual machines. | +| `aksClusterDnsPrefix` | string | `[parameters('name')]` | | Specifies the DNS prefix specified when creating the managed cluster. | +| `aksClusterDnsServiceIP` | string | `''` | | Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr. | +| `aksClusterDockerBridgeCidr` | string | `''` | | Specifies the CIDR notation IP range assigned to the Docker bridge network. It must not overlap with any Subnet IP ranges or the Kubernetes service address range. | +| `aksClusterKubernetesVersion` | string | `''` | | Version of Kubernetes specified when creating the managed cluster. | +| `aksClusterLoadBalancerSku` | string | `'standard'` | `[basic, standard]` | Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools. | +| `aksClusterNetworkPlugin` | string | `''` | `['', azure, kubenet]` | Specifies the network plugin used for building Kubernetes network. - azure or kubenet. | +| `aksClusterNetworkPolicy` | string | `''` | `['', azure, calico]` | Specifies the network policy used for building Kubernetes network. - calico or azure. | +| `aksClusterOutboundType` | string | `'loadBalancer'` | `[loadBalancer, userDefinedRouting]` | Specifies outbound (egress) routing method. - loadBalancer or userDefinedRouting. | +| `aksClusterPodCidr` | string | `''` | | Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used. | +| `aksClusterServiceCidr` | string | `''` | | A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges. | +| `aksClusterSkuTier` | string | `'Free'` | `[Free, Paid]` | Tier of a managed cluster SKU. - Free or Paid. | +| `aksClusterSshPublicKey` | string | `''` | | Specifies the SSH RSA public key string for the Linux nodes. | +| `aksServicePrincipalProfile` | object | `{object}` | | Information about a service principal identity for the cluster to use for manipulating Azure APIs. | +| `authorizedIPRanges` | array | `[]` | | IP ranges are specified in CIDR format, e.g. 137.117.106.88/29. This feature is not compatible with clusters that use Public IP Per Node, or clusters that are using a Basic Load Balancer. | +| `autoScalerProfileBalanceSimilarNodeGroups` | string | `'false'` | `[false, true]` | Specifies the balance of similar node groups for the auto-scaler of the AKS cluster. | +| `autoScalerProfileExpander` | string | `'random'` | `[least-waste, most-pods, priority, random]` | Specifies the expand strategy for the auto-scaler of the AKS cluster. | +| `autoScalerProfileMaxEmptyBulkDelete` | string | `'10'` | | Specifies the maximum empty bulk delete for the auto-scaler of the AKS cluster. | +| `autoScalerProfileMaxGracefulTerminationSec` | string | `'600'` | | Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster. | +| `autoScalerProfileMaxNodeProvisionTime` | string | `'15m'` | | Specifies the maximum node provisioning time for the auto-scaler of the AKS cluster. Values must be an integer followed by an "m". No unit of time other than minutes (m) is supported. | +| `autoScalerProfileMaxTotalUnreadyPercentage` | string | `'45'` | | Specifies the mximum total unready percentage for the auto-scaler of the AKS cluster. The maximum is 100 and the minimum is 0. | +| `autoScalerProfileNewPodScaleUpDelay` | string | `'0s'` | | For scenarios like burst/batch scale where you do not want CA to act before the kubernetes scheduler could schedule all the pods, you can tell CA to ignore unscheduled pods before they are a certain age. Values must be an integer followed by a unit ("s" for seconds, "m" for minutes, "h" for hours, etc). | +| `autoScalerProfileOkTotalUnreadyCount` | string | `'3'` | | Specifies the OK total unready count for the auto-scaler of the AKS cluster. | +| `autoScalerProfileScaleDownDelayAfterAdd` | string | `'10m'` | | Specifies the scale down delay after add of the auto-scaler of the AKS cluster. | +| `autoScalerProfileScaleDownDelayAfterDelete` | string | `'20s'` | | Specifies the scale down delay after delete of the auto-scaler of the AKS cluster. | +| `autoScalerProfileScaleDownDelayAfterFailure` | string | `'3m'` | | Specifies scale down delay after failure of the auto-scaler of the AKS cluster. | +| `autoScalerProfileScaleDownUnneededTime` | string | `'10m'` | | Specifies the scale down unneeded time of the auto-scaler of the AKS cluster. | +| `autoScalerProfileScaleDownUnreadyTime` | string | `'20m'` | | Specifies the scale down unready time of the auto-scaler of the AKS cluster. | +| `autoScalerProfileScanInterval` | string | `'10s'` | | Specifies the scan interval of the auto-scaler of the AKS cluster. | +| `autoScalerProfileSkipNodesWithLocalStorage` | string | `'true'` | `[false, true]` | Specifies if nodes with local storage should be skipped for the auto-scaler of the AKS cluster. | +| `autoScalerProfileSkipNodesWithSystemPods` | string | `'true'` | `[false, true]` | Specifies if nodes with system pods should be skipped for the auto-scaler of the AKS cluster. | +| `autoScalerProfileUtilizationThreshold` | string | `'0.5'` | | Specifies the utilization threshold of the auto-scaler of the AKS cluster. | +| `azurePolicyEnabled` | bool | `True` | | Specifies whether the azurepolicy add-on is enabled or not. For security reasons, this setting should be enabled. | +| `azurePolicyVersion` | string | `'v2'` | | Specifies the azure policy version to use. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[cluster-autoscaler, guard, kube-apiserver, kube-audit, kube-audit-admin, kube-controller-manager, kube-scheduler]` | `[cluster-autoscaler, guard, kube-apiserver, kube-audit, kube-audit-admin, kube-controller-manager, kube-scheduler]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableLocalAccounts` | bool | `False` | | If set to true, getting static credentials will be disabled for this cluster. This must only be used on Managed Clusters that are AAD enabled. | +| `disableRunCommand` | bool | `False` | | Whether to disable run command for the cluster or not. | +| `diskEncryptionSetID` | string | `''` | | The resource ID of the disc encryption set to apply to the clsuter. For security reasons, this value should be provided. | +| `enableAzureDefender` | bool | `False` | | Whether to enable Azure Defender. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableKeyvaultSecretsProvider` | bool | `False` | | Specifies whether the KeyvaultSecretsProvider add-on is enabled or not. | +| `enableOidcIssuerProfile` | bool | `False` | | Whether the The OIDC issuer profile of the Managed Cluster is enabled. | +| `enablePodSecurityPolicy` | bool | `False` | | Whether to enable Kubernetes pod security policy. | +| `enablePrivateCluster` | bool | `False` | | Specifies whether to create the cluster as a private cluster or not. | +| `enablePrivateClusterPublicFQDN` | bool | `False` | | Whether to create additional public FQDN for private cluster or not. | +| `enableRBAC` | bool | `True` | | Whether to enable Kubernetes Role-Based Access Control. | +| `enableSecretRotation` | string | `'false'` | `[false, true]` | Specifies whether the KeyvaultSecretsProvider add-on uses secret rotation. | +| `httpApplicationRoutingEnabled` | bool | `False` | | Specifies whether the httpApplicationRouting add-on is enabled or not. | +| `ingressApplicationGatewayEnabled` | bool | `False` | | Specifies whether the ingressApplicationGateway (AGIC) add-on is enabled or not. | +| `kubeDashboardEnabled` | bool | `False` | | Specifies whether the kubeDashboard add-on is enabled or not. | +| `location` | string | `[resourceGroup().location]` | | Specifies the location of AKS cluster. It picks up Resource Group's location by default. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedOutboundIPCount` | int | `0` | | Outbound IP Count for the Load balancer. | +| `monitoringWorkspaceId` | string | `''` | | Resource ID of the monitoring log analytics workspace. | +| `nodeResourceGroup` | string | `[format('{0}_aks_{1}_nodes', resourceGroup().name, parameters('name'))]` | | Name of the resource group containing agent pool nodes. | +| `omsAgentEnabled` | bool | `True` | | Specifies whether the OMS agent is enabled. | +| `podIdentityProfileAllowNetworkPluginKubenet` | bool | `False` | | Running in Kubenet is disabled by default due to the security related nature of AAD Pod Identity and the risks of IP spoofing. | +| `podIdentityProfileEnable` | bool | `False` | | Whether the pod identity addon is enabled. | +| `podIdentityProfileUserAssignedIdentities` | array | `[]` | | The pod identities to use in the cluster. | +| `podIdentityProfileUserAssignedIdentityExceptions` | array | `[]` | | The pod identity exceptions to allow. | +| `roleAssignments` | array | `[]` | | 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'. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `usePrivateDNSZone` | bool | `False` | | If AKS will create a Private DNS Zone in the Node Resource Group. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `primaryAgentPoolProfile` + +Provide values for primary agent pool as needed. +For available properties check + +

+ +Parameter JSON format + +```json +"primaryAgentPoolProfile": { + "value": [ + { + "name": "poolname", + "vmSize": "Standard_DS3_v2", + "osDiskSizeGB": 128, + "count": 2, + "osType": "Linux", + "maxCount": 5, + "minCount": 1, + "enableAutoScaling": true, + "scaleSetPriority": "Regular", + "scaleSetEvictionPolicy": "Delete", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "type": "VirtualMachineScaleSets", + "availabilityZones": [ + "1", + "2", + "3" + ], + "maxPods": 30, + "storageProfile": "ManagedDisks", + "mode": "System", + "vnetSubnetID": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet", + "tags": { + "Owner": "test.user@testcompany.com", + "BusinessUnit": "IaCs", + "Environment": "PROD", + "Region": "USEast" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +primaryAgentPoolProfile: [ + { + name: 'poolname' + vmSize: 'Standard_DS3_v2' + osDiskSizeGB: 128 + count: 2 + osType: 'Linux' + maxCount: 5 + minCount: 1 + enableAutoScaling: true + scaleSetPriority: 'Regular' + scaleSetEvictionPolicy: 'Delete' + nodeLabels: {} + nodeTaints: [ + 'CriticalAddonsOnly=true:NoSchedule' + ] + type: 'VirtualMachineScaleSets' + availabilityZones: [ + '1' + '2' + '3' + ] + maxPods: 30 + storageProfile: 'ManagedDisks' + mode: 'System' + vnetSubnetID: '/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet' + tags: { + Owner: 'test.user@testcompany.com' + BusinessUnit: 'IaCs' + Environment: 'PROD' + Region: 'USEast' + } + } +] +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `controlPlaneFQDN` | string | The control plane FQDN of the managed cluster. | +| `kubeletidentityObjectId` | string | The Object ID of the AKS identity. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the managed cluster. | +| `omsagentIdentityObjectId` | string | The Object ID of the OMS agent identity. | +| `resourceGroupName` | string | The resource group the managed cluster was deployed into. | +| `resourceId` | string | The resource ID of the managed cluster. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Azure

+ +
+ +via Bicep module + +```bicep +module managedClusters './Microsoft.ContainerService/managedClusters/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ManagedClusters' + params: { + // Required parameters + name: '<>-az-aks-azure-001' + primaryAgentPoolProfile: [ + { + availabilityZones: [ + '1' + ] + count: 1 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + mode: 'System' + name: 'systempool' + osDiskSizeGB: 0 + osType: 'Linux' + serviceCidr: '' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + vnetSubnetID: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Primary' + } + ] + // Non-required parameters + agentPools: [ + { + availabilityZones: [ + '1' + ] + count: 2 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + minPods: 2 + mode: 'User' + name: 'userpool1' + nodeLabels: {} + nodeTaints: [ + 'CriticalAddonsOnly=true:NoSchedule' + ] + osDiskSizeGB: 128 + osType: 'Linux' + scaleSetEvictionPolicy: 'Delete' + scaleSetPriority: 'Regular' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + vnetSubnetID: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Secondary' + } + { + availabilityZones: [ + '1' + ] + count: 2 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + minPods: 2 + mode: 'User' + name: 'userpool2' + nodeLabels: {} + nodeTaints: [ + 'CriticalAddonsOnly=true:NoSchedule' + ] + osDiskSizeGB: 128 + osType: 'Linux' + scaleSetEvictionPolicy: 'Delete' + scaleSetPriority: 'Regular' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + vnetSubnetID: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Tertiary' + } + ] + aksClusterNetworkPlugin: 'azure' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + diskEncryptionSetID: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/diskEncryptionSets/adp-<>-az-des-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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": "<>-az-aks-azure-001" + }, + "primaryAgentPoolProfile": { + "value": [ + { + "availabilityZones": [ + "1" + ], + "count": 1, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "mode": "System", + "name": "systempool", + "osDiskSizeGB": 0, + "osType": "Linux", + "serviceCidr": "", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Primary" + } + ] + }, + // Non-required parameters + "agentPools": { + "value": [ + { + "availabilityZones": [ + "1" + ], + "count": 2, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "minPods": 2, + "mode": "User", + "name": "userpool1", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "osDiskSizeGB": 128, + "osType": "Linux", + "scaleSetEvictionPolicy": "Delete", + "scaleSetPriority": "Regular", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Secondary" + }, + { + "availabilityZones": [ + "1" + ], + "count": 2, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "minPods": 2, + "mode": "User", + "name": "userpool2", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "osDiskSizeGB": 128, + "osType": "Linux", + "scaleSetEvictionPolicy": "Delete", + "scaleSetPriority": "Regular", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2", + "vnetSubnetID": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-aks/subnets/Tertiary" + } + ] + }, + "aksClusterNetworkPlugin": { + "value": "azure" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diskEncryptionSetID": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/diskEncryptionSets/adp-<>-az-des-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

+ +

Example 2: Kubenet

+ +
+ +via Bicep module + +```bicep +module managedClusters './Microsoft.ContainerService/managedClusters/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ManagedClusters' + params: { + // Required parameters + name: '<>-az-aks-kubenet-001' + primaryAgentPoolProfile: [ + { + availabilityZones: [ + '1' + ] + count: 1 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + mode: 'System' + name: 'systempool' + osDiskSizeGB: 0 + osType: 'Linux' + serviceCidr: '' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + } + ] + // Non-required parameters + agentPools: [ + { + availabilityZones: [ + '1' + ] + count: 2 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + minPods: 2 + mode: 'User' + name: 'userpool1' + nodeLabels: {} + nodeTaints: [ + 'CriticalAddonsOnly=true:NoSchedule' + ] + osDiskSizeGB: 128 + osType: 'Linux' + scaleSetEvictionPolicy: 'Delete' + scaleSetPriority: 'Regular' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + } + { + availabilityZones: [ + '1' + ] + count: 2 + enableAutoScaling: true + maxCount: 3 + maxPods: 30 + minCount: 1 + minPods: 2 + mode: 'User' + name: 'userpool2' + nodeLabels: {} + nodeTaints: [ + 'CriticalAddonsOnly=true:NoSchedule' + ] + osDiskSizeGB: 128 + osType: 'Linux' + scaleSetEvictionPolicy: 'Delete' + scaleSetPriority: 'Regular' + storageProfile: 'ManagedDisks' + type: 'VirtualMachineScaleSets' + vmSize: 'Standard_DS2_v2' + } + ] + aksClusterNetworkPlugin: 'kubenet' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-aks-kubenet-001" + }, + "primaryAgentPoolProfile": { + "value": [ + { + "availabilityZones": [ + "1" + ], + "count": 1, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "mode": "System", + "name": "systempool", + "osDiskSizeGB": 0, + "osType": "Linux", + "serviceCidr": "", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2" + } + ] + }, + // Non-required parameters + "agentPools": { + "value": [ + { + "availabilityZones": [ + "1" + ], + "count": 2, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "minPods": 2, + "mode": "User", + "name": "userpool1", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "osDiskSizeGB": 128, + "osType": "Linux", + "scaleSetEvictionPolicy": "Delete", + "scaleSetPriority": "Regular", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2" + }, + { + "availabilityZones": [ + "1" + ], + "count": 2, + "enableAutoScaling": true, + "maxCount": 3, + "maxPods": 30, + "minCount": 1, + "minPods": 2, + "mode": "User", + "name": "userpool2", + "nodeLabels": {}, + "nodeTaints": [ + "CriticalAddonsOnly=true:NoSchedule" + ], + "osDiskSizeGB": 128, + "osType": "Linux", + "scaleSetEvictionPolicy": "Delete", + "scaleSetPriority": "Regular", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets", + "vmSize": "Standard_DS2_v2" + } + ] + }, + "aksClusterNetworkPlugin": { + "value": "kubenet" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ContainerService/managedClusters/version.json b/modules/Microsoft.ContainerService/managedClusters/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.ContainerService/managedClusters/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..be9528a --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(flexibleServer.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: flexibleServer +}] diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/min.parameters.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/min.parameters.json new file mode 100644 index 0000000..c2f6ed4 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/min.parameters.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-postgresqlflexserver-min-001" + }, + "skuName": { + "value": "Standard_B2s" + }, + "tier": { + "value": "Burstable" + }, + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + } + } +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/private.parameters.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/private.parameters.json new file mode 100644 index 0000000..ed8f972 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/private.parameters.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-postgresqlflexserver-private-001" + }, + "skuName": { + "value": "Standard_D2s_v3" + }, + "tier": { + "value": "GeneralPurpose" + }, + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "geoRedundantBackup": { + "value": "Enabled" + }, + "delegatedSubnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-postgres/subnets/<>-az-subnet-x-postgres" + }, + "privateDnsZoneArmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/<>.postgres.database.azure.com" + }, + "databases": { + "value": [ + { + "name": "testdb1", + "collation": "en_US.utf8", + "charset": "UTF8" + }, + { + "name": "testdb2" + } + ] + }, + "configurations": { + "value": [ + { + "name": "log_min_messages", + "source": "user-override", + "value": "INFO" + }, + { + "name": "autovacuum_naptime", + "source": "user-override", + "value": "80" + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/public.parameters.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/public.parameters.json new file mode 100644 index 0000000..8eacc69 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/.test/public.parameters.json @@ -0,0 +1,102 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-postgresqlflexserver-public-001" + }, + "skuName": { + "value": "Standard_D2s_v3" + }, + "tier": { + "value": "GeneralPurpose" + }, + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "availabilityZone": { + "value": "2" + }, + "backupRetentionDays": { + "value": 20 + }, + "geoRedundantBackup": { + "value": "Enabled" + }, + "storageSizeGB": { + "value": 1024 + }, + "version": { + "value": "14" + }, + "highAvailability": { + "value": "SameZone" + }, + "location": { + "value": "westeurope" + }, + "firewallRules": { + "value": [ + { + "name": "AllowAllWindowsAzureIps", + "endIpAddress": "0.0.0.0", + "startIpAddress": "0.0.0.0" + }, + { + "name": "test-rule1", + "startIpAddress": "10.10.10.1", + "endIpAddress": "10.10.10.10" + }, + { + "name": "test-rule2", + "startIpAddress": "100.100.100.1", + "endIpAddress": "100.100.100.10" + } + ] + }, + "databases": { + "value": [ + { + "name": "testdb1", + "collation": "en_US.utf8", + "charset": "UTF8" + }, + { + "name": "testdb2" + } + ] + }, + "configurations": { + "value": [ + { + "name": "log_min_messages", + "source": "user-override", + "value": "INFO" + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/deploy.bicep b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/deploy.bicep new file mode 100644 index 0000000..8ff4afb --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/deploy.bicep @@ -0,0 +1,51 @@ +@description('Required. The name of the configuration.') +param name string + +@description('Conditional. The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment.') +param flexibleServerName string + +@description('Optional. Source of the configuration.') +param source string = '' + +@description('Optional. Value of the configuration.') +param value string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' existing = { + name: flexibleServerName +} + +resource configuration 'Microsoft.DBforPostgreSQL/flexibleServers/configurations@2022-01-20-preview' = { + name: name + parent: flexibleServer + properties: { + source: !empty(source) ? source : null + value: !empty(value) ? value : null + } +} + +@description('The name of the deployed configuration.') +output name string = configuration.name + +@description('The resource ID of the deployed configuration.') +output resourceId string = configuration.id + +@description('The resource group of the deployed configuration.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/readme.md b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/readme.md new file mode 100644 index 0000000..dc88d78 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/readme.md @@ -0,0 +1,49 @@ +# DBforPostgreSQL FlexibleServers Configurations `[Microsoft.DBforPostgreSQL/flexibleServers/configurations]` + +This module deploys DBforPostgreSQL FlexibleServers Configurations. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DBforPostgreSQL/flexibleServers/configurations` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/configurations) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the configuration. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `flexibleServerName` | string | The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `source` | string | `''` | Source of the configuration. | +| `value` | string | `''` | Value of the configuration. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed configuration. | +| `resourceGroupName` | string | The resource group of the deployed configuration. | +| `resourceId` | string | The resource ID of the deployed configuration. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/version.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/configurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/deploy.bicep b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/deploy.bicep new file mode 100644 index 0000000..8468be4 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/deploy.bicep @@ -0,0 +1,51 @@ +@description('Required. The name of the database.') +param name string + +@description('Conditional. The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment.') +param flexibleServerName string + +@description('Optional. The collation of the database.') +param collation string = '' + +@description('Optional. The charset of the database.') +param charset string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' existing = { + name: flexibleServerName +} + +resource database 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2022-01-20-preview' = { + name: name + parent: flexibleServer + properties: { + collation: !empty(collation) ? collation : null + charset: !empty(charset) ? charset : null + } +} + +@description('The name of the deployed database.') +output name string = database.name + +@description('The resource ID of the deployed database.') +output resourceId string = database.id + +@description('The resource group of the deployed database.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/readme.md b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/readme.md new file mode 100644 index 0000000..4969733 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/readme.md @@ -0,0 +1,49 @@ +# DBforPostgreSQL FlexibleServers Databases `[Microsoft.DBforPostgreSQL/flexibleServers/databases]` + +This module deploys DBforPostgreSQL FlexibleServers Databases. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DBforPostgreSQL/flexibleServers/databases` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/databases) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the database. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `flexibleServerName` | string | The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `charset` | string | `''` | The charset of the database. | +| `collation` | string | `''` | The collation of the database. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database. | +| `resourceGroupName` | string | The resource group of the deployed database. | +| `resourceId` | string | The resource ID of the deployed database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/version.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/databases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep b/modules/Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep new file mode 100644 index 0000000..4c37391 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep @@ -0,0 +1,315 @@ +@description('Required. The name of the PostgreSQL flexible server.') +param name string + +@description('Required. The administrator login name of a server. Can only be specified when the PostgreSQL server is being created.') +param administratorLogin string + +@description('Required. The administrator login password.') +@secure() +param administratorLoginPassword string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The name of the sku, typically, tier + family + cores, e.g. Standard_D4s_v3.') +param skuName string + +@allowed([ + 'GeneralPurpose' + 'Burstable' + 'MemoryOptimized' +]) +@description('Required. The tier of the particular SKU. Tier must align with the "skuName" property. Example, tier cannot be "Burstable" if skuName is "Standard_D4s_v3".') +param tier string + +@allowed([ + '' + '1' + '2' + '3' +]) +@description('Optional. Availability zone information of the server. Default will have no preference set.') +param availabilityZone string = '' + +@minValue(7) +@maxValue(35) +@description('Optional. Backup retention days for the server. Default is 7 days.') +param backupRetentionDays int = 7 + +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. A value indicating whether Geo-Redundant backup is enabled on the server. Default is disabled.') +param geoRedundantBackup string = 'Disabled' + +@allowed([ + 32 + 64 + 128 + 256 + 512 + 1024 + 2048 + 4096 + 8192 + 16384 +]) +@description('Optional. Max storage allowed for a server. Default is 32GB.') +param storageSizeGB int = 32 + +@allowed([ + '11' + '12' + '13' + '14' +]) +@description('Optional. PostgreSQL Server version. Default is 13.') +param version string = '13' + +@allowed([ + 'Disabled' + 'SameZone' + 'ZoneRedundant' +]) +@description('Optional. The mode for high availability. Default is disabled.') +param highAvailability string = 'Disabled' + +@allowed([ + 'Create' + 'Default' + 'PointInTimeRestore' + 'Update' +]) +@description('Optional. The mode to create a new PostgreSQL server. If not provided, will be set to "Default".') +param createMode string = 'Default' + +@description('Optional. Properties for the maintenence window. If provided, "customWindow" property must exist and set to "Enabled".') +param maintenanceWindow object = {} + +@description('Optional. Property required if "createMode" is set to "PointInTimeRestore".') +param pointInTimeUTC string = '' + +@description('Optional. Property required if "createMode" is set to "PointInTimeRestore".') +param sourceServerResourceId string = '' + +@description('Optional. Delegated subnet arm resource ID. Used when the desired connectivity mode is "Private Access" - virtual network integration.') +param delegatedSubnetResourceId string = '' + +@description('Optional. Private dns zone arm resource ID. Used when the desired connectivity mode is "Private Access" and required when "delegatedSubnetResourceId" is used. The Private DNS Zone must be lined to the Virtual Network referenced in "delegatedSubnetResourceId".') +param privateDnsZoneArmResourceId string = '' + +@description('Optional. The firewall rules to create in the PostgreSQL flexible server.') +param firewallRules array = [] + +@description('Optional. The databases to create in the server.') +param databases array = [] + +@description('Optional. The configurations to create in the server.') +param configurations array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = false + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'PostgreSQLLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'PostgreSQLLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' = { + name: name + location: location + tags: tags + sku: { + name: skuName + tier: tier + } + properties: { + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + availabilityZone: availabilityZone + backup: { + backupRetentionDays: backupRetentionDays + geoRedundantBackup: geoRedundantBackup + } + createMode: createMode + highAvailability: { + mode: highAvailability + standbyAvailabilityZone: highAvailability == 'SameZone' ? availabilityZone : null + } + maintenanceWindow: !empty(maintenanceWindow) ? { + customWindow: maintenanceWindow.customWindow + dayOfWeek: maintenanceWindow.customWindow == 'Enabled' ? maintenanceWindow.dayOfWeek : 0 + startHour: maintenanceWindow.customWindow == 'Enabled' ? maintenanceWindow.startHour : 0 + startMinute: maintenanceWindow.customWindow == 'Enabled' ? maintenanceWindow.startMinute : 0 + } : null + network: !empty(delegatedSubnetResourceId) && empty(firewallRules) ? { + delegatedSubnetResourceId: delegatedSubnetResourceId + privateDnsZoneArmResourceId: privateDnsZoneArmResourceId + } : null + pointInTimeUTC: createMode == 'PointInTimeRestore' ? pointInTimeUTC : null + sourceServerResourceId: createMode == 'PointInTimeRestore' ? sourceServerResourceId : null + storage: { + storageSizeGB: storageSizeGB + } + version: version + } +} + +resource flexibleServer_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${flexibleServer.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: flexibleServer +} + +module flexibleServer_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PostgreSQL-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: flexibleServer.id + } +}] + +module flexibleServer_databases 'databases/deploy.bicep' = [for (database, index) in databases: { + name: '${uniqueString(deployment().name, location)}-PostgreSQL-DB-${index}' + params: { + name: database.name + flexibleServerName: flexibleServer.name + collation: contains(database, 'collation') ? database.collation : '' + charset: contains(database, 'charset') ? database.charset : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module flexibleServer_firewallRules 'firewallRules/deploy.bicep' = [for (firewallRule, index) in firewallRules: { + name: '${uniqueString(deployment().name, location)}-PostgreSQL-FirewallRules-${index}' + params: { + name: firewallRule.name + flexibleServerName: flexibleServer.name + startIpAddress: firewallRule.startIpAddress + endIpAddress: firewallRule.endIpAddress + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module flexibleServer_configurations 'configurations/deploy.bicep' = [for (configuration, index) in configurations: { + name: '${uniqueString(deployment().name, location)}-PostgreSQL-Configurations-${index}' + params: { + name: configuration.name + flexibleServerName: flexibleServer.name + source: contains(configuration, 'source') ? configuration.source : '' + value: contains(configuration, 'value') ? configuration.value : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource flexibleServer_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: flexibleServer +} + +@description('The name of the deployed PostgreSQL Flexible server.') +output name string = flexibleServer.name + +@description('The resource ID of the deployed PostgreSQL Flexible server.') +output resourceId string = flexibleServer.id + +@description('The resource group of the deployed PostgreSQL Flexible server.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = flexibleServer.location diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/deploy.bicep b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/deploy.bicep new file mode 100644 index 0000000..879e234 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/deploy.bicep @@ -0,0 +1,48 @@ +@description('Required. The name of the PostgreSQL flexible server Firewall Rule.') +param name string + +@description('Required. The start IP address of the firewall rule. Must be IPv4 format. Use value \'0.0.0.0\' for all Azure-internal IP addresses.') +param startIpAddress string + +@description('Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value \'0.0.0.0\' for all Azure-internal IP addresses.') +param endIpAddress string + +@description('Conditional. The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment.') +param flexibleServerName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' existing = { + name: flexibleServerName +} + +resource firewallRule 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2022-01-20-preview' = { + name: name + parent: flexibleServer + properties: { + endIpAddress: endIpAddress + startIpAddress: startIpAddress + } +} + +@description('The name of the deployed firewall rule.') +output name string = firewallRule.name + +@description('The resource ID of the deployed firewall rule.') +output resourceId string = firewallRule.id + +@description('The resource group of the deployed firewall rule.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/readme.md b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/readme.md new file mode 100644 index 0000000..46039cd --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/readme.md @@ -0,0 +1,48 @@ +# DBforPostgreSQL FlexibleServers FirewallRules `[Microsoft.DBforPostgreSQL/flexibleServers/firewallRules]` + +This module deploys DBforPostgreSQL FlexibleServers FirewallRules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DBforPostgreSQL/flexibleServers/firewallRules` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/firewallRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `endIpAddress` | string | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses. | +| `name` | string | The name of the PostgreSQL flexible server Firewall Rule. | +| `startIpAddress` | string | The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `flexibleServerName` | string | The name of the parent PostgreSQL flexible server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed firewall rule. | +| `resourceGroupName` | string | The resource group of the deployed firewall rule. | +| `resourceId` | string | The resource ID of the deployed firewall rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/version.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/firewallRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/readme.md b/modules/Microsoft.DBforPostgreSQL/flexibleServers/readme.md new file mode 100644 index 0000000..5ce3207 --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/readme.md @@ -0,0 +1,731 @@ +# DBforPostgreSQL FlexibleServers `[Microsoft.DBforPostgreSQL/flexibleServers]` + +This module deploys DBforPostgreSQL FlexibleServers. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DBforPostgreSQL/flexibleServers` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers) | +| `Microsoft.DBforPostgreSQL/flexibleServers/configurations` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/configurations) | +| `Microsoft.DBforPostgreSQL/flexibleServers/databases` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/databases) | +| `Microsoft.DBforPostgreSQL/flexibleServers/firewallRules` | [2022-01-20-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DBforPostgreSQL/2022-01-20-preview/flexibleServers/firewallRules) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `administratorLogin` | string | | The administrator login name of a server. Can only be specified when the PostgreSQL server is being created. | +| `administratorLoginPassword` | secureString | | The administrator login password. | +| `name` | string | | The name of the PostgreSQL flexible server. | +| `skuName` | string | | The name of the sku, typically, tier + family + cores, e.g. Standard_D4s_v3. | +| `tier` | string | `[Burstable, GeneralPurpose, MemoryOptimized]` | The tier of the particular SKU. Tier must align with the "skuName" property. Example, tier cannot be "Burstable" if skuName is "Standard_D4s_v3". | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `availabilityZone` | string | `''` | `['', 1, 2, 3]` | Availability zone information of the server. Default will have no preference set. | +| `backupRetentionDays` | int | `7` | | Backup retention days for the server. Default is 7 days. | +| `configurations` | _[configurations](configurations/readme.md)_ array | `[]` | | The configurations to create in the server. | +| `createMode` | string | `'Default'` | `[Create, Default, PointInTimeRestore, Update]` | The mode to create a new PostgreSQL server. If not provided, will be set to "Default". | +| `databases` | _[databases](databases/readme.md)_ array | `[]` | | The databases to create in the server. | +| `delegatedSubnetResourceId` | string | `''` | | Delegated subnet arm resource ID. Used when the desired connectivity mode is "Private Access" - virtual network integration. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[PostgreSQLLogs]` | `[PostgreSQLLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `False` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `firewallRules` | _[firewallRules](firewallRules/readme.md)_ array | `[]` | | The firewall rules to create in the PostgreSQL flexible server. | +| `geoRedundantBackup` | string | `'Disabled'` | `[Disabled, Enabled]` | A value indicating whether Geo-Redundant backup is enabled on the server. Default is disabled. | +| `highAvailability` | string | `'Disabled'` | `[Disabled, SameZone, ZoneRedundant]` | The mode for high availability. Default is disabled. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maintenanceWindow` | object | `{object}` | | Properties for the maintenence window. If provided, "customWindow" property must exist and set to "Enabled". | +| `pointInTimeUTC` | string | `''` | | Property required if "createMode" is set to "PointInTimeRestore". | +| `privateDnsZoneArmResourceId` | string | `''` | | Private dns zone arm resource ID. Used when the desired connectivity mode is "Private Access" and required when "delegatedSubnetResourceId" is used. The Private DNS Zone must be lined to the Virtual Network referenced in "delegatedSubnetResourceId". | +| `roleAssignments` | array | `[]` | | 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'. | +| `sourceServerResourceId` | string | `''` | | Property required if "createMode" is set to "PointInTimeRestore". | +| `storageSizeGB` | int | `32` | `[32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384]` | Max storage allowed for a server. Default is 32GB. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `version` | string | `'13'` | `[11, 12, 13, 14]` | PostgreSQL Server version. Default is 13. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `firewallRules` + +To enable firewall rules on the PostgreSQL flexible server: + +- Used when the desired connectivity mode is "Public Access" only. + +

+ +Parameter JSON format + +```json +"firewallRules": { + // Example showing all available fields + "value": [ + { + "name": "AllowAllWindowsAzureIps", //Use this rule to allow Trusted Azure services to access the server + "endIpAddress": "0.0.0.0", + "startIpAddress": "0.0.0.0" + }, + { + "name": "test-rule1", + "startIpAddress": "10.10.10.1", //Start IP address for the firewall rule. Must be IPv4 format + "endIpAddress": "10.10.10.10" //End IP address for the firewall rule. Must be IPv4 format + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +firewallRules: [ + // Example showing all available fields + { + name: 'AllowAllWindowsAzureIps', //Use this rule to allow Trusted Azure services to access the server + endIpAddress: '0.0.0.0' + startIpAddress: '0.0.0.0' + } + { + name: "test-rule1", + startIpAddress: '10.10.10.1' //Start IP address for the firewall rule. Must be IPv4 format + endIpAddress: '10.10.10.10' //End IP address for the firewall rule. Must be IPv4 format + } +] +``` + +
+

+ +### Parameter Usage: `configurations` + +To override default server configurations on the PostgreSQL flexible server: + +- Use the following documentation as guidance for the available configurations: [PostgreSQL Server Configurations](https://docs.microsoft.com/en-us/azure/postgresql/single-server/how-to-configure-server-parameters-using-cli). + +

+ +Parameter JSON format + +```json +"configurations": { + // Example showing all available fields + "value": [ + { + "name": "log_min_messages", // Name of the configuration + "source": "user-override", // user-override, dynamic, system-default + "value": "INFO" // Value of the configuration + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +configurations: [ + // Example showing all available fields + { + name: 'log_min_messages' // Name of the configuration + source: 'user-override' // user-override, dynamic, system-default + value: 'INFO' // Value of the configuration + } +] +``` + +
+

+ +### Parameter Usage: `databases` + +To create databases on the PostgreSQL flexible server: + +

+ +Parameter JSON format + +```json +"databases": { + // Example showing all available fields + "value": [ + { + "name": "testdb1", // Name of the database + "collation": "en_US.utf8", // Collation of the database + "charset": "UTF8" // Character set of the database + }, + { + "name": "testdb2" // Name of the database only which implements the default collation and charset + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +databases: [ + // Example showing all available fields + { + name: 'testdb1' // Name of the database + collation: 'en_US.utf8' // Collation of the database + charset: 'UTF8' // Character set of the database + } + { + name: 'testdb2' // Name of the database only which implements the default collation and charset + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed PostgreSQL Flexible server. | +| `resourceGroupName` | string | The resource group of the deployed PostgreSQL Flexible server. | +| `resourceId` | string | The resource ID of the deployed PostgreSQL Flexible server. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module flexibleServers './Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FlexibleServers' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + name: '<>-az-postgresqlflexserver-min-001' + skuName: 'Standard_B2s' + tier: 'Burstable' + } +} +``` + +
+

+ +

+ +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 + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "name": { + "value": "<>-az-postgresqlflexserver-min-001" + }, + "skuName": { + "value": "Standard_B2s" + }, + "tier": { + "value": "Burstable" + } + } +} +``` + +
+

+ +

Example 2: Private

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module flexibleServers './Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FlexibleServers' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + name: '<>-az-postgresqlflexserver-private-001' + skuName: 'Standard_D2s_v3' + tier: 'GeneralPurpose' + // Non-required parameters + configurations: [ + { + name: 'log_min_messages' + source: 'user-override' + value: 'INFO' + } + { + name: 'autovacuum_naptime' + source: 'user-override' + value: '80' + } + ] + databases: [ + { + charset: 'UTF8' + collation: 'en_US.utf8' + name: 'testdb1' + } + { + name: 'testdb2' + } + ] + delegatedSubnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-postgres/subnets/<>-az-subnet-x-postgres' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + geoRedundantBackup: 'Enabled' + privateDnsZoneArmResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/<>.postgres.database.azure.com' + } +} +``` + +
+

+ +

+ +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 + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "name": { + "value": "<>-az-postgresqlflexserver-private-001" + }, + "skuName": { + "value": "Standard_D2s_v3" + }, + "tier": { + "value": "GeneralPurpose" + }, + // Non-required parameters + "configurations": { + "value": [ + { + "name": "log_min_messages", + "source": "user-override", + "value": "INFO" + }, + { + "name": "autovacuum_naptime", + "source": "user-override", + "value": "80" + } + ] + }, + "databases": { + "value": [ + { + "charset": "UTF8", + "collation": "en_US.utf8", + "name": "testdb1" + }, + { + "name": "testdb2" + } + ] + }, + "delegatedSubnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-postgres/subnets/<>-az-subnet-x-postgres" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "geoRedundantBackup": { + "value": "Enabled" + }, + "privateDnsZoneArmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/<>.postgres.database.azure.com" + } + } +} +``` + +
+

+ +

Example 3: Public

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module flexibleServers './Microsoft.DBforPostgreSQL/flexibleServers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FlexibleServers' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + name: '<>-az-postgresqlflexserver-public-001' + skuName: 'Standard_D2s_v3' + tier: 'GeneralPurpose' + // Non-required parameters + availabilityZone: '2' + backupRetentionDays: 20 + configurations: [ + { + name: 'log_min_messages' + source: 'user-override' + value: 'INFO' + } + ] + databases: [ + { + charset: 'UTF8' + collation: 'en_US.utf8' + name: 'testdb1' + } + { + name: 'testdb2' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + firewallRules: [ + { + endIpAddress: '0.0.0.0' + name: 'AllowAllWindowsAzureIps' + startIpAddress: '0.0.0.0' + } + { + endIpAddress: '10.10.10.10' + name: 'test-rule1' + startIpAddress: '10.10.10.1' + } + { + endIpAddress: '100.100.100.10' + name: 'test-rule2' + startIpAddress: '100.100.100.1' + } + ] + geoRedundantBackup: 'Enabled' + highAvailability: 'SameZone' + location: 'westeurope' + storageSizeGB: 1024 + version: '14' + } +} +``` + +
+

+ +

+ +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 + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "name": { + "value": "<>-az-postgresqlflexserver-public-001" + }, + "skuName": { + "value": "Standard_D2s_v3" + }, + "tier": { + "value": "GeneralPurpose" + }, + // Non-required parameters + "availabilityZone": { + "value": "2" + }, + "backupRetentionDays": { + "value": 20 + }, + "configurations": { + "value": [ + { + "name": "log_min_messages", + "source": "user-override", + "value": "INFO" + } + ] + }, + "databases": { + "value": [ + { + "charset": "UTF8", + "collation": "en_US.utf8", + "name": "testdb1" + }, + { + "name": "testdb2" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "firewallRules": { + "value": [ + { + "endIpAddress": "0.0.0.0", + "name": "AllowAllWindowsAzureIps", + "startIpAddress": "0.0.0.0" + }, + { + "endIpAddress": "10.10.10.10", + "name": "test-rule1", + "startIpAddress": "10.10.10.1" + }, + { + "endIpAddress": "100.100.100.10", + "name": "test-rule2", + "startIpAddress": "100.100.100.1" + } + ] + }, + "geoRedundantBackup": { + "value": "Enabled" + }, + "highAvailability": { + "value": "SameZone" + }, + "location": { + "value": "westeurope" + }, + "storageSizeGB": { + "value": 1024 + }, + "version": { + "value": "14" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DBforPostgreSQL/flexibleServers/version.json b/modules/Microsoft.DBforPostgreSQL/flexibleServers/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.DBforPostgreSQL/flexibleServers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.DataFactory/factories/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DataFactory/factories/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ef6e10c --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + '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 dataFactory 'Microsoft.DataFactory/factories@2018-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(dataFactory.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: dataFactory +}] diff --git a/modules/Microsoft.DataFactory/factories/.test/min.parameters.json b/modules/Microsoft.DataFactory/factories/.test/min.parameters.json new file mode 100644 index 0000000..f432bf3 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/.test/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": "<>-adf-min-001" + } + } +} diff --git a/modules/Microsoft.DataFactory/factories/.test/parameters.json b/modules/Microsoft.DataFactory/factories/.test/parameters.json new file mode 100644 index 0000000..6c7759a --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/.test/parameters.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-adf-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "managedVirtualNetworkName": { + "value": "default" + }, + "managedPrivateEndpoints": { + "value": [ + { + "name": "adp<>azsax001-managed-privateEndpoint", + "groupId": "blob", + "fqdns": [ + "adp<>azsax001.blob.core.windows.net" + ], + "privateLinkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ] + }, + "integrationRuntimes": { + "value": [ + { + "name": "AutoResolveIntegrationRuntime", + "type": "Managed", + "managedVirtualNetworkName": "default", + "typeProperties": { + "computeProperties": { + "location": "AutoResolve" + } + } + }, + { + "name": "TestRuntime", + "type": "SelfHosted" + } + ] + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "gitConfigureLater": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "dataFactory", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.datafactory.azure.net" + ] + } + } + ] + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + } + } +} diff --git a/modules/Microsoft.DataFactory/factories/deploy.bicep b/modules/Microsoft.DataFactory/factories/deploy.bicep new file mode 100644 index 0000000..d92f171 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/deploy.bicep @@ -0,0 +1,300 @@ +@description('Required. The name of the Azure Factory to create.') +param name string + +@description('Optional. The name of the Managed Virtual Network.') +param managedVirtualNetworkName string = '' + +@description('Optional. An array of managed private endpoints objects created in the Data Factory managed virtual network.') +param managedPrivateEndpoints array = [] + +@description('Optional. An array of objects for the configuration of an Integration Runtime.') +param integrationRuntimes array = [] + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. Boolean to define whether or not to configure git during template deployment.') +param gitConfigureLater bool = true + +@description('Optional. Repository type - can be \'FactoryVSTSConfiguration\' or \'FactoryGitHubConfiguration\'. Default is \'FactoryVSTSConfiguration\'.') +param gitRepoType string = 'FactoryVSTSConfiguration' + +@description('Optional. The account name.') +param gitAccountName string = '' + +@description('Optional. The project name. Only relevant for \'FactoryVSTSConfiguration\'.') +param gitProjectName string = '' + +@description('Optional. The repository name.') +param gitRepositoryName string = '' + +@description('Optional. The collaboration branch name. Default is \'main\'.') +param gitCollaborationBranch string = 'main' + +@description('Optional. The root folder path name. Default is \'/\'.') +param gitRootFolder string = '/' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Optional. User assigned identity to use when fetching the customer managed key.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ActivityRuns' + 'PipelineRuns' + 'TriggerRuns' + 'SSISPackageEventMessages' + 'SSISPackageExecutableStatistics' + 'SSISPackageEventMessageContext' + 'SSISPackageExecutionComponentPhases' + 'SSISPackageExecutionDataStatistics' + 'SSISIntegrationRuntimeLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ActivityRuns' + 'PipelineRuns' + 'TriggerRuns' + 'SSISPackageEventMessages' + 'SSISPackageExecutableStatistics' + 'SSISPackageEventMessageContext' + 'SSISPackageExecutionComponentPhases' + 'SSISPackageExecutionDataStatistics' + 'SSISIntegrationRuntimeLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' = { + name: name + location: location + tags: tags + identity: identity + properties: { + repoConfiguration: bool(gitConfigureLater) ? null : union({ + type: gitRepoType + accountName: gitAccountName + repositoryName: gitRepositoryName + collaborationBranch: gitCollaborationBranch + rootFolder: gitRootFolder + }, (gitRepoType == 'FactoryVSTSConfiguration' ? { + projectName: gitProjectName + } : {}), {}) + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : null) + encryption: !empty(cMKKeyName) ? { + identity: { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } + keyName: cMKKeyName + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : null + vaultBaseUrl: cMKKeyVault.properties.vaultUri + } : null + } +} + +module dataFactory_managedVirtualNetwork 'managedVirtualNetworks/deploy.bicep' = if (!empty(managedVirtualNetworkName)) { + name: '${uniqueString(deployment().name, location)}-DataFactory-ManagedVNet' + params: { + name: managedVirtualNetworkName + dataFactoryName: dataFactory.name + managedPrivateEndpoints: managedPrivateEndpoints + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module dataFactory_integrationRuntimes 'integrationRuntimes/deploy.bicep' = [for (integrationRuntime, index) in integrationRuntimes: { + name: '${uniqueString(deployment().name, location)}-DataFactory-IntegrationRuntime-${index}' + params: { + dataFactoryName: dataFactory.name + name: integrationRuntime.name + type: integrationRuntime.type + managedVirtualNetworkName: contains(integrationRuntime, 'managedVirtualNetworkName') ? integrationRuntime.managedVirtualNetworkName : '' + typeProperties: contains(integrationRuntime, 'typeProperties') ? integrationRuntime.typeProperties : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + dataFactory_managedVirtualNetwork + ] +}] + +resource dataFactory_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${dataFactory.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: dataFactory +} + +resource dataFactory_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: dataFactory +} + +module dataFactory_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-DataFactory-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: dataFactory.id + } +}] + +module dataFactory_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-DataFactory-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(dataFactory.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: dataFactory.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The Name of the Azure Data Factory instance.') +output name string = dataFactory.name + +@description('The Resource ID of the Data factory.') +output resourceId string = dataFactory.id + +@description('The name of the Resource Group with the Data factory.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(dataFactory.identity, 'principalId') ? dataFactory.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = dataFactory.location diff --git a/modules/Microsoft.DataFactory/factories/integrationRuntimes/deploy.bicep b/modules/Microsoft.DataFactory/factories/integrationRuntimes/deploy.bicep new file mode 100644 index 0000000..3b12f49 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/integrationRuntimes/deploy.bicep @@ -0,0 +1,63 @@ +@description('Conditional. The name of the parent Azure Data Factory. Required if the template is used in a standalone deployment.') +param dataFactoryName string + +@description('Required. The name of the Integration Runtime.') +param name string + +@allowed([ + 'Managed' + 'SelfHosted' +]) +@description('Required. The type of Integration Runtime.') +param type string + +@description('Optional. The name of the Managed Virtual Network if using type "Managed" .') +param managedVirtualNetworkName string = '' + +@description('Optional. Integration Runtime type properties. Required if type is "Managed".') +param typeProperties object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var managedVirtualNetwork_var = { + referenceName: type == 'Managed' ? managedVirtualNetworkName : null + type: type == 'Managed' ? 'ManagedVirtualNetworkReference' : null +} + +resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' existing = { + name: dataFactoryName +} + +resource integrationRuntime 'Microsoft.DataFactory/factories/integrationRuntimes@2018-06-01' = { + name: name + parent: dataFactory + properties: type == 'Managed' ? { + type: type + managedVirtualNetwork: managedVirtualNetwork_var + typeProperties: typeProperties + } : { + type: type + } +} + +@description('The name of the Resource Group the Integration Runtime was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the Integration Runtime.') +output name string = integrationRuntime.name + +@description('The resource ID of the Integration Runtime.') +output resourceId string = integrationRuntime.id diff --git a/modules/Microsoft.DataFactory/factories/integrationRuntimes/readme.md b/modules/Microsoft.DataFactory/factories/integrationRuntimes/readme.md new file mode 100644 index 0000000..def1e61 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/integrationRuntimes/readme.md @@ -0,0 +1,80 @@ +# Data Factory Integration RunTimes `[Microsoft.DataFactory/factories/integrationRuntimes]` + +This module deploys a Managed or Self-Hosted Integration Runtime for an Azure Data Factory + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DataFactory/factories/integrationRuntimes` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/integrationRuntimes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | The name of the Integration Runtime. | +| `type` | string | `[Managed, SelfHosted]` | The type of Integration Runtime. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `dataFactoryName` | string | The name of the parent Azure Data Factory. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `managedVirtualNetworkName` | string | `''` | The name of the Managed Virtual Network if using type "Managed" . | +| `typeProperties` | object | `{object}` | Integration Runtime type properties. Required if type is "Managed". | + + +### Parameter Usage: [`typeProperties`](https://docs.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories/integrationruntimes?tabs=bicep#integrationruntime-objects) + +

+ +Parameter JSON format + +```json +"typeProperties": { + "value": { + "computeProperties": { + "location": "AutoResolve" + } + } +} +``` + +
+ +Bicep format + +```bicep +typeProperties: { + computeProperties: { + location: 'AutoResolve' + } +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Integration Runtime. | +| `resourceGroupName` | string | The name of the Resource Group the Integration Runtime was created in. | +| `resourceId` | string | The resource ID of the Integration Runtime. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DataFactory/factories/integrationRuntimes/version.json b/modules/Microsoft.DataFactory/factories/integrationRuntimes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/integrationRuntimes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/deploy.bicep b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/deploy.bicep new file mode 100644 index 0000000..1523bd7 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/deploy.bicep @@ -0,0 +1,57 @@ +@description('Conditional. The name of the parent Azure Data Factory. Required if the template is used in a standalone deployment.') +param dataFactoryName string + +@description('Required. The name of the Managed Virtual Network.') +param name string + +@description('Optional. An array of managed private endpoints objects created in the Data Factory managed virtual network.') +param managedPrivateEndpoints array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' existing = { + name: dataFactoryName +} + +resource managedVirtualNetwork 'Microsoft.DataFactory/factories/managedVirtualNetworks@2018-06-01' = { + name: name + parent: dataFactory + properties: {} +} + +module managedVirtualNetwork_managedPrivateEndpoint 'managedPrivateEndpoints/deploy.bicep' = [for (managedPrivateEndpoint, index) in managedPrivateEndpoints: { + name: '${deployment().name}-managedPrivateEndpoint-${index}' + params: { + dataFactoryName: dataFactoryName + managedVirtualNetworkName: name + name: managedPrivateEndpoint.name + fqdns: managedPrivateEndpoint.fqdns + groupId: managedPrivateEndpoint.groupId + privateLinkResourceId: managedPrivateEndpoint.privateLinkResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the Resource Group the Managed Virtual Network was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the Managed Virtual Network.') +output name string = managedVirtualNetwork.name + +@description('The resource ID of the Managed Virtual Network.') +output resourceId string = managedVirtualNetwork.id diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/deploy.bicep b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/deploy.bicep new file mode 100644 index 0000000..e2d8a07 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/deploy.bicep @@ -0,0 +1,59 @@ +@description('Conditional. The name of the parent data factory. Required if the template is used in a standalone deployment.') +param dataFactoryName string + +@description('Required. The name of the parent managed virtual network.') +param managedVirtualNetworkName string + +@description('Required. The managed private endpoint resource name.') +param name string + +@description('Required. The groupId to which the managed private endpoint is created.') +param groupId string + +@description('Required. Fully qualified domain names.') +param fqdns array + +@description('Required. The ARM resource ID of the resource to which the managed private endpoint is created.') +param privateLinkResourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' existing = { + name: dataFactoryName + + resource managedVirtualNetwork 'managedVirtualNetworks@2018-06-01' existing = { + name: managedVirtualNetworkName + } +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedPrivateEndpoint 'Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints@2018-06-01' = { + name: name + parent: datafactory::managedVirtualNetwork + properties: { + fqdns: fqdns + groupId: groupId + privateLinkResourceId: privateLinkResourceId + } +} + +@description('The name of the deployed managed private endpoint.') +output name string = managedPrivateEndpoint.name + +@description('The resource ID of the deployed managed private endpoint.') +output resourceId string = managedPrivateEndpoint.id + +@description('The resource group of the deployed managed private endpoint.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/readme.md b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/readme.md new file mode 100644 index 0000000..b0ec7fe --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/readme.md @@ -0,0 +1,50 @@ +# DataFactory Factories ManagedVirtualNetwork ManagedPrivateEndpoints `[Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints]` + +This module deploys a Managed Private Endpoint in a Managed Virtual Network for an Azure Data Factory + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks/managedPrivateEndpoints) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `fqdns` | array | Fully qualified domain names. | +| `groupId` | string | The groupId to which the managed private endpoint is created. | +| `managedVirtualNetworkName` | string | The name of the parent managed virtual network. | +| `name` | string | The managed private endpoint resource name. | +| `privateLinkResourceId` | string | The ARM resource ID of the resource to which the managed private endpoint is created. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `dataFactoryName` | string | The name of the parent data factory. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed private endpoint. | +| `resourceGroupName` | string | The resource group of the deployed managed private endpoint. | +| `resourceId` | string | The resource ID of the deployed managed private endpoint. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/version.json b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/readme.md b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/readme.md new file mode 100644 index 0000000..e34d8c5 --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/readme.md @@ -0,0 +1,97 @@ +# Data Factory Managed Virtual Network `[Microsoft.DataFactory/factories/managedVirtualNetworks]` + +This module deploys a Managed Virtual Network for an Azure Data Factory + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DataFactory/factories/managedVirtualNetworks` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks) | +| `Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks/managedPrivateEndpoints) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Managed Virtual Network. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `dataFactoryName` | string | The name of the parent Azure Data Factory. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `managedPrivateEndpoints` | _[managedPrivateEndpoints](managedPrivateEndpoints/readme.md)_ array | `[]` | An array of managed private endpoints objects created in the Data Factory managed virtual network. | + + +### Parameter Usage: `managedPrivateEndpoints` + +To use Managed Private Endpoints the following dependencies must be deployed: + +- Destination private link resource must be created before and permissions allow requesting a private link connection to that resource. + +

+ +Parameter JSON format + +```json +"managedPrivateEndpoints": { + "value": [ + { + "name": "mystorageaccount-managed-privateEndpoint", // Required: The managed private endpoint resource name + "groupId": "blob", // Required: The groupId to which the managed private endpoint is created + "fqdns": [ + "mystorageaccount.blob.core.windows.net" // Required: Fully qualified domain names + ], + "privateLinkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/mystorageaccount" + // Required: The ARM resource ID of the resource to which the managed private endpoint is created. + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +managedPrivateEndpoints: [ + // Example showing all available fields + { + name: 'mystorageaccount-managed-privateEndpoint' // Required: The managed private endpoint resource name + groupId: 'blob' // Required: The groupId to which the managed private endpoint is created + fqdns: [ + 'mystorageaccount.blob.core.windows.net' // Required: Fully qualified domain names + ] + privateLinkResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/mystorageaccount' + } // Required: The ARM resource ID of the resource to which the managed private endpoint is created. +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Managed Virtual Network. | +| `resourceGroupName` | string | The name of the Resource Group the Managed Virtual Network was created in. | +| `resourceId` | string | The resource ID of the Managed Virtual Network. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/version.json b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/managedVirtualNetworks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DataFactory/factories/readme.md b/modules/Microsoft.DataFactory/factories/readme.md new file mode 100644 index 0000000..23efb9d --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/readme.md @@ -0,0 +1,591 @@ +# Data Factories `[Microsoft.DataFactory/factories]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DataFactory/factories` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories) | +| `Microsoft.DataFactory/factories/integrationRuntimes` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/integrationRuntimes) | +| `Microsoft.DataFactory/factories/managedVirtualNetworks` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks) | +| `Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks/managedPrivateEndpoints) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Azure Factory to create. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `cMKUserAssignedIdentityResourceId` | string | `''` | | User assigned identity to use when fetching the customer managed key. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ActivityRuns, PipelineRuns, SSISIntegrationRuntimeLogs, SSISPackageEventMessageContext, SSISPackageEventMessages, SSISPackageExecutableStatistics, SSISPackageExecutionComponentPhases, SSISPackageExecutionDataStatistics, TriggerRuns]` | `[ActivityRuns, PipelineRuns, SSISIntegrationRuntimeLogs, SSISPackageEventMessageContext, SSISPackageEventMessages, SSISPackageExecutableStatistics, SSISPackageExecutionComponentPhases, SSISPackageExecutionDataStatistics, TriggerRuns]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `gitAccountName` | string | `''` | | The account name. | +| `gitCollaborationBranch` | string | `'main'` | | The collaboration branch name. Default is 'main'. | +| `gitConfigureLater` | bool | `True` | | Boolean to define whether or not to configure git during template deployment. | +| `gitProjectName` | string | `''` | | The project name. Only relevant for 'FactoryVSTSConfiguration'. | +| `gitRepositoryName` | string | `''` | | The repository name. | +| `gitRepoType` | string | `'FactoryVSTSConfiguration'` | | Repository type - can be 'FactoryVSTSConfiguration' or 'FactoryGitHubConfiguration'. Default is 'FactoryVSTSConfiguration'. | +| `gitRootFolder` | string | `'/'` | | The root folder path name. Default is '/'. | +| `integrationRuntimes` | _[integrationRuntimes](integrationRuntimes/readme.md)_ array | `[]` | | An array of objects for the configuration of an Integration Runtime. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedPrivateEndpoints` | array | `[]` | | An array of managed private endpoints objects created in the Data Factory managed virtual network. | +| `managedVirtualNetworkName` | string | `''` | | The name of the Managed Virtual Network. | +| `privateEndpoints` | array | `[]` | | Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | 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'. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `managedPrivateEndpoints` + +To use Managed Private Endpoints the following dependencies must be deployed: + +- The `managedVirtualNetworkName` property must be set to allow provisioning of a managed virtual network in Azure Data Factory. +- Destination private link resource must be created before and permissions allow requesting a private link connection to that resource. + +

+ +Parameter JSON format + +```json +"managedPrivateEndpoints": { + "value": [ + { + "name": "mystorageaccount-managed-privateEndpoint", // Required: The managed private endpoint resource name + "groupId": "blob", // Required: The groupId to which the managed private endpoint is created + "fqdns": [ + "mystorageaccount.blob.core.windows.net" // Required: Fully qualified domain names + ], + "privateLinkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/mystorageaccount" + // Required: The ARM resource ID of the resource to which the managed private endpoint is created. + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +managedPrivateEndpoints: [ + // Example showing all available fields + { + name: 'mystorageaccount-managed-privateEndpoint' // Required: The managed private endpoint resource name + groupId: 'blob' // Required: The groupId to which the managed private endpoint is created + fqdns: [ + 'mystorageaccount.blob.core.windows.net' // Required: Fully qualified domain names + ] + privateLinkResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/mystorageaccount' + } // Required: The ARM resource ID of the resource to which the managed private endpoint is created. +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the Azure Data Factory instance. | +| `resourceGroupName` | string | The name of the Resource Group with the Data factory. | +| `resourceId` | string | The Resource ID of the Data factory. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module factories './Microsoft.DataFactory/factories/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Factories' + params: { + name: '<>-adf-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-adf-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module factories './Microsoft.DataFactory/factories/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Factories' + params: { + // Required parameters + name: '<>-adf-001' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + gitConfigureLater: true + integrationRuntimes: [ + { + managedVirtualNetworkName: 'default' + name: 'AutoResolveIntegrationRuntime' + type: 'Managed' + typeProperties: { + computeProperties: { + location: 'AutoResolve' + } + } + } + { + name: 'TestRuntime' + type: 'SelfHosted' + } + ] + lock: 'CanNotDelete' + managedPrivateEndpoints: [ + { + fqdns: [ + 'adp<>azsax001.blob.core.windows.net' + ] + groupId: 'blob' + name: 'adp<>azsax001-managed-privateEndpoint' + privateLinkResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + } + ] + managedVirtualNetworkName: 'default' + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.datafactory.azure.net' + ] + } + service: 'dataFactory' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + publicNetworkAccess: 'Disabled' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-adf-001" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "gitConfigureLater": { + "value": true + }, + "integrationRuntimes": { + "value": [ + { + "managedVirtualNetworkName": "default", + "name": "AutoResolveIntegrationRuntime", + "type": "Managed", + "typeProperties": { + "computeProperties": { + "location": "AutoResolve" + } + } + }, + { + "name": "TestRuntime", + "type": "SelfHosted" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "managedPrivateEndpoints": { + "value": [ + { + "fqdns": [ + "adp<>azsax001.blob.core.windows.net" + ], + "groupId": "blob", + "name": "adp<>azsax001-managed-privateEndpoint", + "privateLinkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + ] + }, + "managedVirtualNetworkName": { + "value": "default" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.datafactory.azure.net" + ] + }, + "service": "dataFactory", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DataFactory/factories/version.json b/modules/Microsoft.DataFactory/factories/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DataFactory/factories/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DataProtection/backupVaults/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DataProtection/backupVaults/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..dd41a22 --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,58 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') +} + +resource backupVault 'Microsoft.DataProtection/backupVaults@2022-03-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(backupVault.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: backupVault +}] diff --git a/modules/Microsoft.DataProtection/backupVaults/.test/min.parameters.json b/modules/Microsoft.DataProtection/backupVaults/.test/min.parameters.json new file mode 100644 index 0000000..6b44ecf --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/.test/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": "<>-az-bv-min-001" + } + } +} diff --git a/modules/Microsoft.DataProtection/backupVaults/.test/parameters.json b/modules/Microsoft.DataProtection/backupVaults/.test/parameters.json new file mode 100644 index 0000000..bad76a8 --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/.test/parameters.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-bv-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "backupPolicies": { + "value": [ + { + "name": "DefaultPolicy", + "properties": { + "policyRules": [ + { + "backupParameters": { + "backupType": "Incremental", + "objectType": "AzureBackupParams" + }, + "trigger": { + "schedule": { + "repeatingTimeIntervals": [ + "R/2022-05-31T23:30:00+01:00/P1D" + ], + "timeZone": "W. Europe Standard Time" + }, + "taggingCriteria": [ + { + "tagInfo": { + "tagName": "Default", + "id": "Default_" + }, + "taggingPriority": 99, + "isDefault": true + } + ], + "objectType": "ScheduleBasedTriggerContext" + }, + "dataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + }, + "name": "BackupDaily", + "objectType": "AzureBackupRule" + }, + { + "lifecycles": [ + { + "deleteAfter": { + "objectType": "AbsoluteDeleteOption", + "duration": "P7D" + }, + "targetDataStoreCopySettings": [], + "sourceDataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + } + } + ], + "isDefault": true, + "name": "Default", + "objectType": "AzureRetentionRule" + } + ], + "datasourceTypes": [ + "Microsoft.Compute/disks" + ], + "objectType": "BackupPolicy" + } + } + ] + } + } +} diff --git a/modules/Microsoft.DataProtection/backupVaults/backupPolicies/deploy.bicep b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/deploy.bicep new file mode 100644 index 0000000..7861a1f --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/deploy.bicep @@ -0,0 +1,42 @@ +@description('Required. The name of the backup vault.') +param backupVaultName string + +@description('Optional. The name of the backup policy.') +param name string = 'DefaultPolicy' + +@description('Optional. The properties of the backup policy.') +param properties object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource backupVault 'Microsoft.DataProtection/backupVaults@2022-03-01' existing = { + name: backupVaultName +} + +resource backupPolicy 'Microsoft.DataProtection/backupVaults/backupPolicies@2022-03-01' = { + name: name + parent: backupVault + properties: properties +} + +@description('The name of the backup policy.') +output name string = backupPolicy.name + +@description('The resource ID of the backup policy.') +output resourceId string = backupPolicy.id + +@description('The name of the resource group the backup policy was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DataProtection/backupVaults/backupPolicies/readme.md b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/readme.md new file mode 100644 index 0000000..cbb84de --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/readme.md @@ -0,0 +1,181 @@ +# DataProtection BackupVaults BackupPolicies `[Microsoft.DataProtection/backupVaults/backupPolicies]` + +This module deploys DataProtection BackupVaults BackupPolicies. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DataProtection/backupVaults/backupPolicies` | [2022-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataProtection/2022-03-01/backupVaults/backupPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `backupVaultName` | string | The name of the backup vault. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'DefaultPolicy'` | The name of the backup policy. | +| `properties` | object | `{object}` | The properties of the backup policy. | + + +### Parameter Usage: `properties` + +Create a backup policy. + +

+ +Parameter JSON format + +```json + "properties": { + "value": { + "policyRules": [ + { + "backupParameters": { + "backupType": "Incremental", + "objectType": "AzureBackupParams" + }, + "trigger": { + "schedule": { + "repeatingTimeIntervals": [ + "R/2022-05-31T23:30:00+01:00/P1D" + ], + "timeZone": "W. Europe Standard Time" + }, + "taggingCriteria": [ + { + "tagInfo": { + "tagName": "Default", + "id": "Default_" + }, + "taggingPriority": 99, + "isDefault": true + } + ], + "objectType": "ScheduleBasedTriggerContext" + }, + "dataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + }, + "name": "BackupDaily", + "objectType": "AzureBackupRule" + }, + { + "lifecycles": [ + { + "deleteAfter": { + "objectType": "AbsoluteDeleteOption", + "duration": "P7D" + }, + "targetDataStoreCopySettings": [], + "sourceDataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + } + } + ], + "isDefault": true, + "name": "Default", + "objectType": "AzureRetentionRule" + } + ], + "datasourceTypes": [ + "Microsoft.Compute/disks" + ], + "objectType": "BackupPolicy" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +properties: { + policyRules: [ + { + backupParameters: { + backupType: 'Incremental' + objectType: 'AzureBackupParams' + } + trigger: { + schedule: { + repeatingTimeIntervals: [ + 'R/2022-05-31T23:30:00+01:00/P1D' + ] + timeZone: 'W. Europe Standard Time' + } + taggingCriteria: [ + { + tagInfo: { + tagName: 'Default' + id: 'Default_' + } + taggingPriority: 99 + isDefault: true + } + ] + objectType: 'ScheduleBasedTriggerContext' + } + dataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + name: 'BackupDaily' + objectType: 'AzureBackupRule' + } + { + lifecycles: [ + { + deleteAfter: { + objectType: 'AbsoluteDeleteOption' + duration: 'P7D' + } + targetDataStoreCopySettings: [] + sourceDataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + } + ] + isDefault: true + name: 'Default' + objectType: 'AzureRetentionRule' + } + ] + datasourceTypes: [ + 'Microsoft.Compute/disks' + ] + objectType: 'BackupPolicy' +} +``` + +
+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backup policy. | +| `resourceGroupName` | string | The name of the resource group the backup policy was created in. | +| `resourceId` | string | The resource ID of the backup policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DataProtection/backupVaults/backupPolicies/version.json b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/version.json new file mode 100644 index 0000000..bfb2819 --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/backupPolicies/version.json @@ -0,0 +1,3 @@ +{ + "version": "0.4" +} diff --git a/modules/Microsoft.DataProtection/backupVaults/deploy.bicep b/modules/Microsoft.DataProtection/backupVaults/deploy.bicep new file mode 100644 index 0000000..e940b43 --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/deploy.bicep @@ -0,0 +1,129 @@ +@description('Required. Name of the Backup Vault.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Tags of the Recovery Service Vault resource.') +param tags object = {} + +@description('Optional. The datastore type to use.') +@allowed([ + 'ArchiveStore' + 'SnapshotStore' + 'VaultStore' +]) +param dataStoreType string = 'SnapshotStore' + +@description('Optional. The vault redundancy level to use.') +@allowed([ + 'LocallyRedundant' + 'GeoRedundant' +]) +param type string = 'LocallyRedundant' + +@description('Optional. List of all backup policies.') +param backupPolicies array = [] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource backupVault 'Microsoft.DataProtection/backupVaults@2022-03-01' = { + name: name + location: location + tags: tags + identity: any(identity) + properties: { + storageSettings: [ + { + type: type + datastoreType: dataStoreType + } + ] + } +} + +module backupVault_backupPolicies 'backupPolicies/deploy.bicep' = [for (backupPolicy, index) in backupPolicies: { + name: '${uniqueString(deployment().name, location)}-BV-BackupPolicy-${index}' + params: { + backupVaultName: backupVault.name + name: backupPolicy.name + properties: backupPolicy.properties + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource backupVault_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${backupVault.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: backupVault +} + +module backupVault_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-bv-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: backupVault.id + } +}] + +@description('The resource ID of the backup vault.') +output resourceId string = backupVault.id + +@description('The name of the resource group the recovery services vault was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The Name of the backup vault.') +output name string = backupVault.name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(backupVault.identity, 'principalId') ? backupVault.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = backupVault.location diff --git a/modules/Microsoft.DataProtection/backupVaults/readme.md b/modules/Microsoft.DataProtection/backupVaults/readme.md new file mode 100644 index 0000000..342d50c --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/readme.md @@ -0,0 +1,551 @@ +# DataProtection BackupVaults `[Microsoft.DataProtection/backupVaults]` + +This module deploys DataProtection BackupVaults. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DataProtection/backupVaults` | [2022-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataProtection/2022-03-01/backupVaults) | +| `Microsoft.DataProtection/backupVaults/backupPolicies` | [2022-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DataProtection/2022-03-01/backupVaults/backupPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Backup Vault. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backupPolicies` | _[backupPolicies](backupPolicies/readme.md)_ array | `[]` | | List of all backup policies. | +| `dataStoreType` | string | `'SnapshotStore'` | `[ArchiveStore, SnapshotStore, VaultStore]` | The datastore type to use. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Recovery Service Vault resource. | +| `type` | string | `'LocallyRedundant'` | `[GeoRedundant, LocallyRedundant]` | The vault redundancy level to use. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `backupPolicies` + +Create backup policies in the backupvault. + +
+ +Parameter JSON format +```json + "backupPolicies": { + "value": [ + { + "name": "DefaultPolicy", + "properties": { + "policyRules": [ + { + "backupParameters": { + "backupType": "Incremental", + "objectType": "AzureBackupParams" + }, + "trigger": { + "schedule": { + "repeatingTimeIntervals": [ + "R/2022-05-31T23:30:00+01:00/P1D" + ], + "timeZone": "W. Europe Standard Time" + }, + "taggingCriteria": [ + { + "tagInfo": { + "tagName": "Default", + "id": "Default_" + }, + "taggingPriority": 99, + "isDefault": true + } + ], + "objectType": "ScheduleBasedTriggerContext" + }, + "dataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + }, + "name": "BackupDaily", + "objectType": "AzureBackupRule" + }, + { + "lifecycles": [ + { + "deleteAfter": { + "objectType": "AbsoluteDeleteOption", + "duration": "P7D" + }, + "targetDataStoreCopySettings": [], + "sourceDataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + } + } + ], + "isDefault": true, + "name": "Default", + "objectType": "AzureRetentionRule" + } + ], + "datasourceTypes": [ + "Microsoft.Compute/disks" + ], + "objectType": "BackupPolicy" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +backupPolicies: [ + { + name: 'DefaultPolicy' + properties: { + policyRules: [ + { + backupParameters: { + backupType: 'Incremental' + objectType: 'AzureBackupParams' + } + trigger: { + schedule: { + repeatingTimeIntervals: [ + 'R/2022-05-31T23:30:00+01:00/P1D' + ] + timeZone: 'W. Europe Standard Time' + } + taggingCriteria: [ + { + tagInfo: { + tagName: 'Default' + id: 'Default_' + } + taggingPriority: 99 + isDefault: true + } + ] + objectType: 'ScheduleBasedTriggerContext' + } + dataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + name: 'BackupDaily' + objectType: 'AzureBackupRule' + } + { + lifecycles: [ + { + deleteAfter: { + objectType: 'AbsoluteDeleteOption' + duration: 'P7D' + } + targetDataStoreCopySettings: [] + sourceDataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + } + ] + isDefault: true + name: 'Default' + objectType: 'AzureRetentionRule' + } + ] + datasourceTypes: [ + 'Microsoft.Compute/disks' + ] + objectType: 'BackupPolicy' + } + } +] +``` + +
+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +
+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the backup vault. | +| `resourceGroupName` | string | The name of the resource group the recovery services vault was created in. | +| `resourceId` | string | The resource ID of the backup vault. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module backupVaults './Microsoft.DataProtection/backupVaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BackupVaults' + params: { + name: '<>-az-bv-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-bv-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module backupVaults './Microsoft.DataProtection/backupVaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BackupVaults' + params: { + // Required parameters + name: '<>-az-bv-x-001' + // Non-required parameters + backupPolicies: [ + { + name: 'DefaultPolicy' + properties: { + datasourceTypes: [ + 'Microsoft.Compute/disks' + ] + objectType: 'BackupPolicy' + policyRules: [ + { + backupParameters: { + backupType: 'Incremental' + objectType: 'AzureBackupParams' + } + dataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + name: 'BackupDaily' + objectType: 'AzureBackupRule' + trigger: { + objectType: 'ScheduleBasedTriggerContext' + schedule: { + repeatingTimeIntervals: [ + 'R/2022-05-31T23:30:00+01:00/P1D' + ] + timeZone: 'W. Europe Standard Time' + } + taggingCriteria: [ + { + isDefault: true + taggingPriority: 99 + tagInfo: { + id: 'Default_' + tagName: 'Default' + } + } + ] + } + } + { + isDefault: true + lifecycles: [ + { + deleteAfter: { + duration: 'P7D' + objectType: 'AbsoluteDeleteOption' + } + sourceDataStore: { + dataStoreType: 'OperationalStore' + objectType: 'DataStoreInfoBase' + } + targetDataStoreCopySettings: [] + } + ] + name: 'Default' + objectType: 'AzureRetentionRule' + } + ] + } + } + ] + lock: 'CanNotDelete' + } +} +``` + +
+

+ +

+ +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": "<>-az-bv-x-001" + }, + // Non-required parameters + "backupPolicies": { + "value": [ + { + "name": "DefaultPolicy", + "properties": { + "datasourceTypes": [ + "Microsoft.Compute/disks" + ], + "objectType": "BackupPolicy", + "policyRules": [ + { + "backupParameters": { + "backupType": "Incremental", + "objectType": "AzureBackupParams" + }, + "dataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + }, + "name": "BackupDaily", + "objectType": "AzureBackupRule", + "trigger": { + "objectType": "ScheduleBasedTriggerContext", + "schedule": { + "repeatingTimeIntervals": [ + "R/2022-05-31T23:30:00+01:00/P1D" + ], + "timeZone": "W. Europe Standard Time" + }, + "taggingCriteria": [ + { + "isDefault": true, + "taggingPriority": 99, + "tagInfo": { + "id": "Default_", + "tagName": "Default" + } + } + ] + } + }, + { + "isDefault": true, + "lifecycles": [ + { + "deleteAfter": { + "duration": "P7D", + "objectType": "AbsoluteDeleteOption" + }, + "sourceDataStore": { + "dataStoreType": "OperationalStore", + "objectType": "DataStoreInfoBase" + }, + "targetDataStoreCopySettings": [] + } + ], + "name": "Default", + "objectType": "AzureRetentionRule" + } + ] + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DataProtection/backupVaults/version.json b/modules/Microsoft.DataProtection/backupVaults/version.json new file mode 100644 index 0000000..bfb2819 --- /dev/null +++ b/modules/Microsoft.DataProtection/backupVaults/version.json @@ -0,0 +1,3 @@ +{ + "version": "0.4" +} diff --git a/modules/Microsoft.Databricks/workspaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Databricks/workspaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..fcaa1d5 --- /dev/null +++ b/modules/Microsoft.Databricks/workspaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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 workspace 'Microsoft.Databricks/workspaces@2018-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(workspace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: workspace +}] diff --git a/modules/Microsoft.Databricks/workspaces/.test/parameters.json b/modules/Microsoft.Databricks/workspaces/.test/parameters.json new file mode 100644 index 0000000..dc32749 --- /dev/null +++ b/modules/Microsoft.Databricks/workspaces/.test/parameters.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-adb-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Databricks/workspaces/deploy.bicep b/modules/Microsoft.Databricks/workspaces/deploy.bicep new file mode 100644 index 0000000..45757be --- /dev/null +++ b/modules/Microsoft.Databricks/workspaces/deploy.bicep @@ -0,0 +1,166 @@ +@description('Required. The name of the Azure Databricks workspace to create.') +param name string + +@description('Optional. The managed resource group ID.') +param managedResourceGroupId string = '' + +@description('Optional. The pricing tier of workspace.') +@allowed([ + 'trial' + 'standard' + 'premium' +]) +param pricingTier string = 'premium' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@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. The workspace\'s custom parameters.') +param workspaceParameters object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'dbfs' + 'clusters' + 'accounts' + 'jobs' + 'notebook' + 'ssh' + 'workspace' + 'secrets' + 'sqlPermissions' + 'instancePools' +]) +param diagnosticLogCategoriesToEnable array = [ + 'dbfs' + 'clusters' + 'accounts' + 'jobs' + 'notebook' + 'ssh' + 'workspace' + 'secrets' + 'sqlPermissions' + 'instancePools' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var managedResourceGroupName = '${name}-rg' +var managedResourceGroupId_var = '${subscription().id}/resourceGroups/${managedResourceGroupName}' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.Databricks/workspaces@2018-04-01' = { + name: name + location: location + tags: tags + sku: { + name: pricingTier + } + properties: { + managedResourceGroupId: (empty(managedResourceGroupId) ? managedResourceGroupId_var : managedResourceGroupId) + parameters: workspaceParameters + } +} + +resource workspace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${workspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: workspace +} + +// Note: Diagnostic Settings are only supported by the premium tier +resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if (pricingTier == 'premium' && ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName)))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: workspace +} + +module workspace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-DataBricks-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: workspace.id + } +}] + +@description('The name of the deployed databricks workspace.') +output name string = workspace.name + +@description('The resource ID of the deployed databricks workspace.') +output resourceId string = workspace.id + +@description('The resource group of the deployed databricks workspace.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = workspace.location diff --git a/modules/Microsoft.Databricks/workspaces/readme.md b/modules/Microsoft.Databricks/workspaces/readme.md new file mode 100644 index 0000000..cec0ba1 --- /dev/null +++ b/modules/Microsoft.Databricks/workspaces/readme.md @@ -0,0 +1,313 @@ +# Azure Databricks `[Microsoft.Databricks/workspaces]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Databricks/workspaces` | [2018-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Databricks/2018-04-01/workspaces) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Azure Databricks workspace to create. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[accounts, clusters, dbfs, instancePools, jobs, notebook, secrets, sqlPermissions, ssh, workspace]` | `[accounts, clusters, dbfs, instancePools, jobs, notebook, secrets, sqlPermissions, ssh, workspace]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedResourceGroupId` | string | `''` | | The managed resource group ID. | +| `pricingTier` | string | `'premium'` | `[premium, standard, trial]` | The pricing tier of workspace. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | +| `workspaceParameters` | object | `{object}` | | The workspace's custom parameters. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `customPublicSubnetName` and `customPrivateSubnetName` + +- Require Network Security Groups attached to the subnets (Note: Rule don't have to be set, they are set through the deployment) + +- The two subnets also need the delegation to service `Microsoft.Databricks/workspaces` + +### Parameter Usage: `workspaceParameters` + +- Include only those elements (e.g. amlWorkspaceId) as object if specified, otherwise remove it + +

+ +Parameter JSON format + +```json +"workspaceParameters": { + "value": { + "amlWorkspaceId": { + "value": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.MachineLearningServices/workspaces/xxx" + }, + "customVirtualNetworkId": { + "value": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Network/virtualNetworks/xxx" + }, + "customPublicSubnetName": { + "value": "xxx" + }, + "customPrivateSubnetName": { + "value": "xxx" + }, + "enableNoPublicIp": { + "value": true + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +workspaceParameters: { + amlWorkspaceId: { + value: '/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.MachineLearningServices/workspaces/xxx' + } + customVirtualNetworkId: { + value: '/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Network/virtualNetworks/xxx' + } + customPublicSubnetName: { + value: 'xxx' + } + customPrivateSubnetName: { + value: 'xxx' + } + enableNoPublicIp: { + value: true + } +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed databricks workspace. | +| `resourceGroupName` | string | The resource group of the deployed databricks workspace. | +| `resourceId` | string | The resource ID of the deployed databricks workspace. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Databricks/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + name: '<>-az-adb-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-adb-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Databricks/workspaces/version.json b/modules/Microsoft.Databricks/workspaces/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Databricks/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DesktopVirtualization/applicationgroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..2949623 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + '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 appGroup 'Microsoft.DesktopVirtualization/applicationgroups@2021-07-12' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appGroup +}] diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/min.parameters.json b/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/min.parameters.json new file mode 100644 index 0000000..ecf7a14 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avdag-min-001" + }, + "applicationGroupType": { + "value": "RemoteApp" + }, + "hostpoolName": { + "value": "adp-<>-az-avdhp-x-001" + } + } +} diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/parameters.json b/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/parameters.json new file mode 100644 index 0000000..7e71ce4 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/.test/parameters.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avdag-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "location": { + "value": "westeurope" + }, + "applicationGroupType": { + "value": "RemoteApp" + }, + "hostpoolName": { + "value": "adp-<>-az-avdhp-x-001" + }, + "friendlyName": { + "value": "Remote Applications 1" + }, + "description": { + "value": "This is my first Remote Applications bundle" + }, + "applications": { + "value": [ + { + "name": "notepad", + "description": "Notepad by ARM template", + "friendlyName": "Notepad", + "filePath": "C:\\Windows\\System32\\notepad.exe", + "commandLineSetting": "DoNotAllow", + "commandLineArguments": "", + "showInPortal": true, + "iconPath": "C:\\Windows\\System32\\notepad.exe", + "iconIndex": 0 + }, + { + "name": "wordpad", + "filePath": "C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe", + "friendlyName": "Wordpad" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/deploy.bicep b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/deploy.bicep new file mode 100644 index 0000000..f47f827 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/deploy.bicep @@ -0,0 +1,77 @@ +@sys.description('Conditional. The name of the parent Application Group to create the application(s) in. Required if the template is used in a standalone deployment.') +param appGroupName string + +@sys.description('Required. Name of the Application to be created in the Application Group.') +param name string + +@sys.description('Optional. Description of Application..') +param description string = '' + +@sys.description('Required. Friendly name of Application..') +param friendlyName string + +@sys.description('Required. Specifies a path for the executable file for the application.') +param filePath string + +@allowed([ + 'Allow' + 'DoNotAllow' + 'Require' +]) +@sys.description('Optional. Specifies whether this published application can be launched with command-line arguments provided by the client, command-line arguments specified at publish time, or no command-line arguments at all.') +param commandLineSetting string = 'DoNotAllow' + +@sys.description('Optional. Command-Line Arguments for Application.') +param commandLineArguments string = '' + +@sys.description('Optional. Specifies whether to show the RemoteApp program in the RD Web Access server.') +param showInPortal bool = false + +@sys.description('Optional. Path to icon.') +param iconPath string = '' + +@sys.description('Optional. Index of the icon.') +param iconIndex int = 0 + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2021-09-03-preview' existing = { + name: appGroupName +} + +resource application 'Microsoft.DesktopVirtualization/applicationGroups/applications@2021-07-12' = { + name: name + parent: applicationGroup + properties: { + description: description + friendlyName: friendlyName + filePath: filePath + commandLineSetting: commandLineSetting + commandLineArguments: commandLineArguments + showInPortal: showInPortal + iconPath: iconPath + iconIndex: iconIndex + } +} + +@sys.description('The resource ID of the deployed Application.') +output resourceId string = application.id + +@sys.description('The name of the Resource Group the AVD Application was created in.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The Name of the Application Group to register the Application in.') +output name string = appGroupName diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/readme.md b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/readme.md new file mode 100644 index 0000000..d478d11 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/readme.md @@ -0,0 +1,54 @@ +# AVD Applications `[Microsoft.DesktopVirtualization/applicationGroups/applications]` + +This module deploys AVD Applications. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DesktopVirtualization/applicationGroups/applications` | [2021-07-12](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-07-12/applicationGroups/applications) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `filePath` | string | Specifies a path for the executable file for the application. | +| `friendlyName` | string | Friendly name of Application.. | +| `name` | string | Name of the Application to be created in the Application Group. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appGroupName` | string | The name of the parent Application Group to create the application(s) in. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `commandLineArguments` | string | `''` | | Command-Line Arguments for Application. | +| `commandLineSetting` | string | `'DoNotAllow'` | `[Allow, DoNotAllow, Require]` | Specifies whether this published application can be launched with command-line arguments provided by the client, command-line arguments specified at publish time, or no command-line arguments at all. | +| `description` | string | `''` | | Description of Application.. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `iconIndex` | int | `0` | | Index of the icon. | +| `iconPath` | string | `''` | | Path to icon. | +| `showInPortal` | bool | `False` | | Specifies whether to show the RemoteApp program in the RD Web Access server. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The Name of the Application Group to register the Application in. | +| `resourceGroupName` | string | The name of the Resource Group the AVD Application was created in. | +| `resourceId` | string | The resource ID of the deployed Application. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/version.json b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/applications/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/deploy.bicep b/modules/Microsoft.DesktopVirtualization/applicationgroups/deploy.bicep new file mode 100644 index 0000000..65e1183 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/deploy.bicep @@ -0,0 +1,176 @@ +@sys.description('Required. Name of the Application Group to create this application in.') +@minLength(1) +param name string + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Required. The type of the Application Group to be created. Allowed values: RemoteApp or Desktop.') +@allowed([ + 'RemoteApp' + 'Desktop' +]) +param applicationGroupType string + +@sys.description('Required. Name of the Host Pool to be linked to this Application Group.') +param hostpoolName string + +@sys.description('Optional. The friendly name of the Application Group to be created.') +param friendlyName string = '' + +@sys.description('Optional. The description of the Application Group to be created.') +param description string = '' + +@sys.description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalIds\' 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 = [] + +@sys.description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@sys.description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@sys.description('Optional. Resource ID of log analytics.') +param diagnosticWorkspaceId string = '' + +@sys.description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@sys.description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@sys.description('Optional. Specify the type of lock.') +param lock string = '' + +@sys.description('Optional. Tags of the resource.') +param tags object = {} + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. The name of logs that will be streamed.') +@allowed([ + 'Checkpoint' + 'Error' + 'Management' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Checkpoint' + 'Error' + 'Management' +] + +@sys.description('Optional. List of applications to be created in the Application Group.') +param applications array = [] + +@sys.description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appGroup_hostpool 'Microsoft.DesktopVirtualization/hostpools@2021-07-12' existing = { + name: hostpoolName +} + +resource appGroup 'Microsoft.DesktopVirtualization/applicationgroups@2021-07-12' = { + name: name + location: location + tags: tags + properties: { + hostPoolArmPath: appGroup_hostpool.id + friendlyName: friendlyName + description: description + applicationGroupType: applicationGroupType + } +} + +resource appGroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${appGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: appGroup +} + +resource appGroup_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: appGroup +} + +module appGroup_applications 'applications/deploy.bicep' = [for (application, index) in applications: { + name: '${uniqueString(deployment().name, location)}-AppGroup-App-${index}' + params: { + name: application.name + appGroupName: appGroup.name + description: contains(application, 'description') ? application.description : '' + friendlyName: contains(application, 'friendlyName') ? application.friendlyName : appGroup.name + filePath: application.filePath + commandLineSetting: contains(application, 'commandLineSetting') ? application.commandLineSetting : 'DoNotAllow' + commandLineArguments: contains(application, 'commandLineArguments') ? application.commandLineArguments : '' + showInPortal: contains(application, 'showInPortal') ? application.showInPortal : false + iconPath: contains(application, 'iconPath') ? application.iconPath : application.filePath + iconIndex: contains(application, 'iconIndex') ? application.iconIndex : 0 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module appGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: appGroup.id + } +}] + +@sys.description('The resource ID of the AVD application group.') +output resourceId string = appGroup.id + +@sys.description('The resource group the AVD application group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The name of the AVD application group.') +output name string = appGroup.name + +@sys.description('The location the resource was deployed into.') +output location string = appGroup.location diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/readme.md b/modules/Microsoft.DesktopVirtualization/applicationgroups/readme.md new file mode 100644 index 0000000..b1fe6a0 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/readme.md @@ -0,0 +1,358 @@ +# AVD Application Groups `[Microsoft.DesktopVirtualization/applicationgroups]` + +This module deploys an Azure virtual desktop application group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DesktopVirtualization/applicationGroups` | [2021-07-12](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-07-12/applicationGroups) | +| `Microsoft.DesktopVirtualization/applicationGroups/applications` | [2021-07-12](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-07-12/applicationGroups/applications) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `applicationGroupType` | string | `[Desktop, RemoteApp]` | The type of the Application Group to be created. Allowed values: RemoteApp or Desktop. | +| `hostpoolName` | string | | Name of the Host Pool to be linked to this Application Group. | +| `name` | string | | Name of the Application Group to create this application in. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `applications` | _[applications](applications/readme.md)_ array | `[]` | | List of applications to be created in the Application Group. | +| `description` | string | `''` | | The description of the Application Group to be created. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Checkpoint, Error, Management]` | `[Checkpoint, Error, Management]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of log analytics. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `friendlyName` | string | `''` | | The friendly name of the Application Group to be created. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the AVD application group. | +| `resourceGroupName` | string | The resource group the AVD application group was deployed into. | +| `resourceId` | string | The resource ID of the AVD application group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module applicationgroups './Microsoft.DesktopVirtualization/applicationgroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Applicationgroups' + params: { + // Required parameters + applicationGroupType: 'RemoteApp' + hostpoolName: 'adp-<>-az-avdhp-x-001' + name: '<>-az-avdag-min-001' + } +} +``` + +
+

+ +

+ +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 + "applicationGroupType": { + "value": "RemoteApp" + }, + "hostpoolName": { + "value": "adp-<>-az-avdhp-x-001" + }, + "name": { + "value": "<>-az-avdag-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module applicationgroups './Microsoft.DesktopVirtualization/applicationgroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Applicationgroups' + params: { + // Required parameters + applicationGroupType: 'RemoteApp' + hostpoolName: 'adp-<>-az-avdhp-x-001' + name: '<>-az-avdag-x-001' + // Non-required parameters + applications: [ + { + commandLineArguments: '' + commandLineSetting: 'DoNotAllow' + description: 'Notepad by ARM template' + filePath: 'C:\\Windows\\System32\\notepad.exe' + friendlyName: 'Notepad' + iconIndex: 0 + iconPath: 'C:\\Windows\\System32\\notepad.exe' + name: 'notepad' + showInPortal: true + } + { + filePath: 'C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe' + friendlyName: 'Wordpad' + name: 'wordpad' + } + ] + description: 'This is my first Remote Applications bundle' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + friendlyName: 'Remote Applications 1' + location: 'westeurope' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "applicationGroupType": { + "value": "RemoteApp" + }, + "hostpoolName": { + "value": "adp-<>-az-avdhp-x-001" + }, + "name": { + "value": "<>-az-avdag-x-001" + }, + // Non-required parameters + "applications": { + "value": [ + { + "commandLineArguments": "", + "commandLineSetting": "DoNotAllow", + "description": "Notepad by ARM template", + "filePath": "C:\\Windows\\System32\\notepad.exe", + "friendlyName": "Notepad", + "iconIndex": 0, + "iconPath": "C:\\Windows\\System32\\notepad.exe", + "name": "notepad", + "showInPortal": true + }, + { + "filePath": "C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe", + "friendlyName": "Wordpad", + "name": "wordpad" + } + ] + }, + "description": { + "value": "This is my first Remote Applications bundle" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "friendlyName": { + "value": "Remote Applications 1" + }, + "location": { + "value": "westeurope" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DesktopVirtualization/applicationgroups/version.json b/modules/Microsoft.DesktopVirtualization/applicationgroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/applicationgroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DesktopVirtualization/hostpools/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DesktopVirtualization/hostpools/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..070cef8 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/hostpools/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,77 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + '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 hostPool 'Microsoft.DesktopVirtualization/hostpools@2021-07-12' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(hostPool.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: hostPool +}] diff --git a/modules/Microsoft.DesktopVirtualization/hostpools/.test/parameters.json b/modules/Microsoft.DesktopVirtualization/hostpools/.test/parameters.json new file mode 100644 index 0000000..0762138 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/hostpools/.test/parameters.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avdhp-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "location": { + "value": "westeurope" + }, + "hostpoolFriendlyName": { + "value": "AVDv2" + }, + "hostpoolDescription": { + "value": "My first AVD Host Pool" + }, + "hostpoolType": { + "value": "Pooled" + }, + "personalDesktopAssignmentType": { + "value": "Automatic" + }, + "maxSessionLimit": { + "value": 99999 + }, + "loadBalancerType": { + "value": "BreadthFirst" + }, + "customRdpProperty": { + "value": "audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;" + }, + "vmTemplate": { + "value": { + "domain": "domainname.onmicrosoft.com", + "galleryImageOffer": "office-365", + "galleryImagePublisher": "microsoftwindowsdesktop", + "galleryImageSKU": "20h1-evd-o365pp", + "imageType": "Gallery", + "imageUri": null, + "customImageId": null, + "namePrefix": "avdv2", + "osDiskType": "StandardSSD_LRS", + "useManagedDisks": true, + "vmSize": { + "id": "Standard_D2s_v3", + "cores": 2, + "ram": 8 + } + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DesktopVirtualization/hostpools/deploy.bicep b/modules/Microsoft.DesktopVirtualization/hostpools/deploy.bicep new file mode 100644 index 0000000..beb5fbe --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/hostpools/deploy.bicep @@ -0,0 +1,215 @@ +@description('Required. Name of the Host Pool.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The friendly name of the Host Pool to be created.') +param hostpoolFriendlyName string = '' + +@description('Optional. The description of the Host Pool to be created.') +param hostpoolDescription string = '' + +@description('Optional. Set this parameter to Personal if you would like to enable Persistent Desktop experience. Defaults to Pooled.') +@allowed([ + 'Personal' + 'Pooled' +]) +param hostpoolType string = 'Pooled' + +@description('Optional. Set the type of assignment for a Personal Host Pool type.') +@allowed([ + 'Automatic' + 'Direct' + '' +]) +param personalDesktopAssignmentType string = '' + +@description('Optional. Type of load balancer algorithm.') +@allowed([ + 'BreadthFirst' + 'DepthFirst' + 'Persistent' +]) +param loadBalancerType string = 'BreadthFirst' + +@description('Optional. Maximum number of sessions.') +param maxSessionLimit int = 99999 + +@description('Optional. Host Pool RDP properties.') +param customRdpProperty string = 'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;' + +@description('Optional. Validation host pools allows you to test service changes before they are deployed to production. When set to true, the Host Pool will be deployed in a validation \'ring\' (environment) that receives all the new features (might be less stable). Defaults to false that stands for the stable, production-ready environment.') +param validationEnvironment bool = false + +@description('Optional. The necessary information for adding more VMs to this Host Pool.') +param vmTemplate object = {} + +@description('Optional. Host Pool token validity length. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the token will be valid for 8 hours.') +param tokenValidityLength string = 'PT8H' + +@description('Generated. Do not provide a value! This date value is used to generate a registration token.') +param baseTime string = utcNow('u') + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The type of preferred application group type, default to Desktop Application Group.') +@allowed([ + 'Desktop' + 'None' + 'RailApplications' +]) +param preferredAppGroupType string = 'Desktop' + +@description('Optional. Enable Start VM on connect to allow users to start the virtual machine from a deallocated state. Important: Custom RBAC role required to power manage VMs.') +param startVMOnConnect bool = false + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalIds\' 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. The name of logs that will be streamed.') +@allowed([ + 'Checkpoint' + 'Error' + 'Management' + 'Connection' + 'HostRegistration' + 'AgentHealthStatus' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Checkpoint' + 'Error' + 'Management' + 'Connection' + 'HostRegistration' + 'AgentHealthStatus' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var tokenExpirationTime = dateTimeAdd(baseTime, tokenValidityLength) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource hostPool 'Microsoft.DesktopVirtualization/hostpools@2021-07-12' = { + name: name + location: location + tags: tags + properties: { + friendlyName: hostpoolFriendlyName + description: hostpoolDescription + hostPoolType: hostpoolType + customRdpProperty: customRdpProperty + personalDesktopAssignmentType: any(personalDesktopAssignmentType) + preferredAppGroupType: preferredAppGroupType + maxSessionLimit: maxSessionLimit + loadBalancerType: loadBalancerType + startVMOnConnect: startVMOnConnect + validationEnvironment: validationEnvironment + registrationInfo: { + expirationTime: tokenExpirationTime + token: null + registrationTokenOperation: 'Update' + } + vmTemplate: ((!empty(vmTemplate)) ? null : string(vmTemplate)) + } +} + +resource hostPool_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${hostPool.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: hostPool +} + +resource hostPool_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: hostPool +} + +module hostPool_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-HostPool-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: hostPool.id + } +}] + +@description('The resource ID of the AVD host pool.') +output resourceId string = hostPool.id + +@description('The resource group the AVD host pool was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the AVD host pool.') +output name string = hostPool.name + +@description('The expiration time for the registration token.') +output tokenExpirationTime string = dateTimeAdd(baseTime, tokenValidityLength) + +@description('The location the resource was deployed into.') +output location string = hostPool.location diff --git a/modules/Microsoft.DesktopVirtualization/hostpools/readme.md b/modules/Microsoft.DesktopVirtualization/hostpools/readme.md new file mode 100644 index 0000000..eb05bc8 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/hostpools/readme.md @@ -0,0 +1,417 @@ +# AVD Host Pools `[Microsoft.DesktopVirtualization/hostpools]` + +This module deploys an Azure virtual desktop host pool. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DesktopVirtualization/hostPools` | [2021-07-12](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-07-12/hostPools) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Host Pool. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `customRdpProperty` | string | `'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;'` | | Host Pool RDP properties. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AgentHealthStatus, Checkpoint, Connection, Error, HostRegistration, Management]` | `[AgentHealthStatus, Checkpoint, Connection, Error, HostRegistration, Management]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `hostpoolDescription` | string | `''` | | The description of the Host Pool to be created. | +| `hostpoolFriendlyName` | string | `''` | | The friendly name of the Host Pool to be created. | +| `hostpoolType` | string | `'Pooled'` | `[Personal, Pooled]` | Set this parameter to Personal if you would like to enable Persistent Desktop experience. Defaults to Pooled. | +| `loadBalancerType` | string | `'BreadthFirst'` | `[BreadthFirst, DepthFirst, Persistent]` | Type of load balancer algorithm. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxSessionLimit` | int | `99999` | | Maximum number of sessions. | +| `personalDesktopAssignmentType` | string | `''` | `['', Automatic, Direct]` | Set the type of assignment for a Personal Host Pool type. | +| `preferredAppGroupType` | string | `'Desktop'` | `[Desktop, None, RailApplications]` | The type of preferred application group type, default to Desktop Application Group. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' 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'. | +| `startVMOnConnect` | bool | `False` | | Enable Start VM on connect to allow users to start the virtual machine from a deallocated state. Important: Custom RBAC role required to power manage VMs. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `tokenValidityLength` | string | `'PT8H'` | | Host Pool token validity length. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the token will be valid for 8 hours. | +| `validationEnvironment` | bool | `False` | | Validation host pools allows you to test service changes before they are deployed to production. When set to true, the Host Pool will be deployed in a validation 'ring' (environment) that receives all the new features (might be less stable). Defaults to false that stands for the stable, production-ready environment. | +| `vmTemplate` | object | `{object}` | | The necessary information for adding more VMs to this Host Pool. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Do not provide a value! This date value is used to generate a registration token. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `vmTemplate` + +The below parameter object is converted to an in-line string when handed over to the resource deployment, since that only takes strings. + +

+ +Parameter JSON format + +```json +"vmTemplate": { + "value": { + "domain": ".com", + "galleryImageOffer": "office-365", + "galleryImagePublisher": "microsoftwindowsdesktop", + "galleryImageSKU": "19h2-evd-o365pp", + "imageType": "Gallery", + "imageUri": null, + "customImageId": null, + "namePrefix": "AVDv2", + "osDiskType": "StandardSSD_LRS", + "useManagedDisks": true, + "vmSize": { + "id": "Standard_D2s_v3", + "cores": 2, + "ram": 8 + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +vmTemplate: { + domain: '.com' + galleryImageOffer: 'office-365' + galleryImagePublisher: 'microsoftwindowsdesktop' + galleryImageSKU: '19h2-evd-o365pp' + imageType: 'Gallery' + imageUri: null + customImageId: null + namePrefix: 'AVDv2' + osDiskType: 'StandardSSD_LRS' + useManagedDisks: true + vmSize: { + id: 'Standard_D2s_v3' + cores: 2 + ram: 8 + } +} +``` + +
+

+ +### Parameter Usage: `customRdpProperty` + +

+ +Parameter JSON format + +```json +"customRdpProperty": { + "value": "audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode ID:i:2;" +} +``` + +
+ +
+ +Bicep format + +```bicep +customRdpProperty: 'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode ID:i:2;' +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the AVD host pool. | +| `resourceGroupName` | string | The resource group the AVD host pool was deployed into. | +| `resourceId` | string | The resource ID of the AVD host pool. | +| `tokenExpirationTime` | string | The expiration time for the registration token. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module hostpools './Microsoft.DesktopVirtualization/hostpools/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Hostpools' + params: { + // Required parameters + name: '<>-az-avdhp-x-001' + // Non-required parameters + customRdpProperty: 'audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + hostpoolDescription: 'My first AVD Host Pool' + hostpoolFriendlyName: 'AVDv2' + hostpoolType: 'Pooled' + loadBalancerType: 'BreadthFirst' + location: 'westeurope' + lock: 'CanNotDelete' + maxSessionLimit: 99999 + personalDesktopAssignmentType: 'Automatic' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + vmTemplate: { + customImageId: null + domain: 'domainname.onmicrosoft.com' + galleryImageOffer: 'office-365' + galleryImagePublisher: 'microsoftwindowsdesktop' + galleryImageSKU: '20h1-evd-o365pp' + imageType: 'Gallery' + imageUri: null + namePrefix: 'avdv2' + osDiskType: 'StandardSSD_LRS' + useManagedDisks: true + vmSize: { + cores: 2 + id: 'Standard_D2s_v3' + ram: 8 + } + } + } +} +``` + +
+

+ +

+ +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": "<>-az-avdhp-x-001" + }, + // Non-required parameters + "customRdpProperty": { + "value": "audiocapturemode:i:1;audiomode:i:0;drivestoredirect:s:;redirectclipboard:i:1;redirectcomports:i:1;redirectprinters:i:1;redirectsmartcards:i:1;screen mode id:i:2;" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "hostpoolDescription": { + "value": "My first AVD Host Pool" + }, + "hostpoolFriendlyName": { + "value": "AVDv2" + }, + "hostpoolType": { + "value": "Pooled" + }, + "loadBalancerType": { + "value": "BreadthFirst" + }, + "location": { + "value": "westeurope" + }, + "lock": { + "value": "CanNotDelete" + }, + "maxSessionLimit": { + "value": 99999 + }, + "personalDesktopAssignmentType": { + "value": "Automatic" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "vmTemplate": { + "value": { + "customImageId": null, + "domain": "domainname.onmicrosoft.com", + "galleryImageOffer": "office-365", + "galleryImagePublisher": "microsoftwindowsdesktop", + "galleryImageSKU": "20h1-evd-o365pp", + "imageType": "Gallery", + "imageUri": null, + "namePrefix": "avdv2", + "osDiskType": "StandardSSD_LRS", + "useManagedDisks": true, + "vmSize": { + "cores": 2, + "id": "Standard_D2s_v3", + "ram": 8 + } + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DesktopVirtualization/hostpools/version.json b/modules/Microsoft.DesktopVirtualization/hostpools/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/hostpools/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DesktopVirtualization/scalingplans/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DesktopVirtualization/scalingplans/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..070cef8 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/scalingplans/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,77 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + '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 hostPool 'Microsoft.DesktopVirtualization/hostpools@2021-07-12' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(hostPool.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: hostPool +}] diff --git a/modules/Microsoft.DesktopVirtualization/scalingplans/.test/min.parameters.json b/modules/Microsoft.DesktopVirtualization/scalingplans/.test/min.parameters.json new file mode 100644 index 0000000..e576bf3 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/scalingplans/.test/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": "<>-az-avdsp-x-001" + } + } +} diff --git a/modules/Microsoft.DesktopVirtualization/scalingplans/deploy.bicep b/modules/Microsoft.DesktopVirtualization/scalingplans/deploy.bicep new file mode 100644 index 0000000..09d8c93 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/scalingplans/deploy.bicep @@ -0,0 +1,161 @@ +@description('Required. Name of the scaling plan.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Friendly Name of the scaling plan.') +param friendlyName string = name + +@description('Optional. Description of the scaling plan.') +param scalingplanDescription string = name + +@description('Optional. Timezone to be used for the scaling plan.') +param timeZone string = 'W. Europe Standard Time' + +@allowed([ + 'Pooled' +]) +@description('Optional. The type of hostpool where this scaling plan should be applied.') +param hostPoolType string = 'Pooled' + +@description('Optional. Provide a tag to be used for hosts that should not be affected by the scaling plan.') +param exclusionTag string = '' + +@description('Optional. The schedules related to this scaling plan. If no value is provided a default schedule will be provided.') +param schedules array = [ + { + rampUpStartTime: { + hour: 7 + minute: 0 + } + peakStartTime: { + hour: 9 + minute: 0 + } + rampDownStartTime: { + hour: 18 + minute: 0 + } + offPeakStartTime: { + hour: 20 + minute: 0 + } + name: 'weekdays_schedule' + daysOfWeek: [ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + ] + rampUpLoadBalancingAlgorithm: 'DepthFirst' + rampUpMinimumHostsPct: 20 + rampUpCapacityThresholdPct: 60 + peakLoadBalancingAlgorithm: 'DepthFirst' + rampDownLoadBalancingAlgorithm: 'DepthFirst' + rampDownMinimumHostsPct: 10 + rampDownCapacityThresholdPct: 90 + rampDownForceLogoffUsers: true + rampDownWaitTimeMinutes: 30 + rampDownNotificationMessage: 'You will be logged off in 30 min. Make sure to save your work.' + rampDownStopHostsWhen: 'ZeroSessions' + offPeakLoadBalancingAlgorithm: 'DepthFirst' + } +] + +@description('Optional. An array of references to hostpools.') +param hostPoolReferences array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalIds\' 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. The name of logs that will be streamed.') +@allowed([ + 'Autoscale' +]) +param logsToEnable array = [ + 'Autoscale' +] + +var diagnosticsLogs = [for log in logsToEnable: { + category: log + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource scalingPlan 'Microsoft.DesktopVirtualization/scalingPlans@2021-09-03-preview' = { + name: name + location: location + tags: tags + properties: { + friendlyName: friendlyName + timeZone: timeZone + hostPoolType: hostPoolType + exclusionTag: exclusionTag + schedules: schedules + hostPoolReferences: hostPoolReferences + description: scalingplanDescription + } +} + +resource scalingplan_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: '${scalingPlan.name}-diagnosticsetting' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: scalingPlan +} + +module scalingplan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Workspace-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: scalingPlan.id + } +}] + +@description('The resource ID of the AVD scaling plan.') +output resourceId string = scalingPlan.id + +@description('The resource group the AVD scaling plan was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the AVD scaling plan.') +output name string = scalingPlan.name + +@description('The location the resource was deployed into.') +output location string = scalingPlan.location diff --git a/modules/Microsoft.DesktopVirtualization/scalingplans/readme.md b/modules/Microsoft.DesktopVirtualization/scalingplans/readme.md new file mode 100644 index 0000000..b05a57f --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/scalingplans/readme.md @@ -0,0 +1,305 @@ +# AVD Scaling Plans `[Microsoft.DesktopVirtualization/scalingPlans]` + +This module deploys an AVD Scaling Plan. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DesktopVirtualization/scalingPlans` | [2021-09-03-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-09-03-preview/scalingPlans) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the scaling plan. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `exclusionTag` | string | `''` | | Provide a tag to be used for hosts that should not be affected by the scaling plan. | +| `friendlyName` | string | `[parameters('name')]` | | Friendly Name of the scaling plan. | +| `hostPoolReferences` | array | `[]` | | An array of references to hostpools. | +| `hostPoolType` | string | `'Pooled'` | `[Pooled]` | The type of hostpool where this scaling plan should be applied. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `logsToEnable` | array | `[Autoscale]` | `[Autoscale]` | The name of logs that will be streamed. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' 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'. | +| `scalingplanDescription` | string | `[parameters('name')]` | | Description of the scaling plan. | +| `schedules` | array | `[System.Collections.Hashtable]` | | The schedules related to this scaling plan. If no value is provided a default schedule will be provided. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timeZone` | string | `'W. Europe Standard Time'` | | Timezone to be used for the scaling plan. | + + +### Parameter Usage: `schedules` + +Multiple schedules can be provided as needed. If a schedule is not provided, a default schedule will be created. + +```json +"schedules" : { + "value": [ + { + "rampUpStartTime": { + "hour": 7, + "minute": 0 + }, + "peakStartTime": { + "hour": 9, + "minute": 0 + }, + "rampDownStartTime": { + "hour": 18, + "minute": 0 + }, + "offPeakStartTime": { + "hour": 20, + "minute": 0 + }, + "name": "weekdays_schedule", + "daysOfWeek": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday" + ], + "rampUpLoadBalancingAlgorithm": "DepthFirst", + "rampUpMinimumHostsPct": 20, + "rampUpCapacityThresholdPct": 60, + "peakLoadBalancingAlgorithm": "DepthFirst", + "rampDownLoadBalancingAlgorithm": "DepthFirst", + "rampDownMinimumHostsPct": 10, + "rampDownCapacityThresholdPct": 90, + "rampDownForceLogoffUsers": true, + "rampDownWaitTimeMinutes": 30, + "rampDownNotificationMessage": "You will be logged off in 30 min. Make sure to save your work.", + "rampDownStopHostsWhen": "ZeroSessions", + "offPeakLoadBalancingAlgorithm": "DepthFirst" + } + ] +} +``` + +

+ +
+ +Bicep format + +```bicep +'schedules': [ + { + rampUpStartTime: { + hour: 7 + minute: 0 + } + peakStartTime: { + hour: 9 + minute: 0 + } + rampDownStartTime: { + hour: 18 + minute: 0 + } + offPeakStartTime: { + hour: 20 + minute: 0 + } + name: 'weekdays_schedule' + daysOfWeek: [ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + ] + rampUpLoadBalancingAlgorithm: 'DepthFirst' + rampUpMinimumHostsPct: 20 + rampUpCapacityThresholdPct: 60 + peakLoadBalancingAlgorithm: 'DepthFirst' + rampDownLoadBalancingAlgorithm: 'DepthFirst' + rampDownMinimumHostsPct: 10 + rampDownCapacityThresholdPct: 90 + rampDownForceLogoffUsers: true + rampDownWaitTimeMinutes: 30 + rampDownNotificationMessage: 'You will be logged off in 30 min. Make sure to save your work.' + rampDownStopHostsWhen: 'ZeroSessions' + offPeakLoadBalancingAlgorithm: 'DepthFirst' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the AVD scaling plan. | +| `resourceGroupName` | string | The resource group the AVD scaling plan was deployed into. | +| `resourceId` | string | The resource ID of the AVD scaling plan. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module scalingplans './Microsoft.DesktopVirtualization/scalingplans/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Scalingplans' + params: { + name: '<>-az-avdsp-x-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avdsp-x-001" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DesktopVirtualization/scalingplans/version.json b/modules/Microsoft.DesktopVirtualization/scalingplans/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/scalingplans/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DesktopVirtualization/workspaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DesktopVirtualization/workspaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..065443b --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/workspaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,73 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + '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 workspace 'Microsoft.DesktopVirtualization/workspaces@2021-07-12' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(workspace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: workspace +}] diff --git a/modules/Microsoft.DesktopVirtualization/workspaces/.test/parameters.json b/modules/Microsoft.DesktopVirtualization/workspaces/.test/parameters.json new file mode 100644 index 0000000..5ffb007 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/workspaces/.test/parameters.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-avdws-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "location": { + "value": "westeurope" + }, + "appGroupResourceIds": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.DesktopVirtualization/applicationgroups/adp-<>-az-avdag-x-001" + ] + }, + "workspaceFriendlyName": { + "value": "My first AVD Workspace" + }, + "workspaceDescription": { + "value": "This is my first AVD Workspace" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DesktopVirtualization/workspaces/deploy.bicep b/modules/Microsoft.DesktopVirtualization/workspaces/deploy.bicep new file mode 100644 index 0000000..8879c53 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/workspaces/deploy.bicep @@ -0,0 +1,143 @@ +@description('Required. The name of the workspace to be attach to new Application Group.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Resource IDs for the existing Application groups this workspace will group together.') +param appGroupResourceIds array = [] + +@description('Optional. The friendly name of the Workspace to be created.') +param workspaceFriendlyName string = '' + +@description('Optional. The description of the Workspace to be created.') +param workspaceDescription string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalIds\' 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. The name of logs that will be streamed.') +@allowed([ + 'Checkpoint' + 'Error' + 'Management' + 'Feed' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Checkpoint' + 'Error' + 'Management' + 'Feed' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.DesktopVirtualization/workspaces@2021-07-12' = { + name: name + location: location + tags: tags + properties: { + applicationGroupReferences: appGroupResourceIds + description: workspaceDescription + friendlyName: workspaceFriendlyName + } +} + +resource workspace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${workspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: workspace +} + +resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: workspace +} + +module workspace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Workspace-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: workspace.id + } +}] + +@description('The resource ID of the AVD workspace.') +output resourceId string = workspace.id + +@description('The resource group the AVD workspace was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the AVD workspace.') +output name string = workspace.name + +@description('The location the resource was deployed into.') +output location string = workspace.location diff --git a/modules/Microsoft.DesktopVirtualization/workspaces/readme.md b/modules/Microsoft.DesktopVirtualization/workspaces/readme.md new file mode 100644 index 0000000..35d2006 --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/workspaces/readme.md @@ -0,0 +1,270 @@ +# AVD Workspaces `[Microsoft.DesktopVirtualization/workspaces]` + +This module deploys an Azure virtual desktop workspace. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DesktopVirtualization/workspaces` | [2021-07-12](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2021-07-12/workspaces) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appGroupResourceIds` | array | Resource IDs for the existing Application groups this workspace will group together. | +| `name` | string | The name of the workspace to be attach to new Application Group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Checkpoint, Error, Feed, Management]` | `[Checkpoint, Error, Feed, Management]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' 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}` | | Tags of the resource. | +| `workspaceDescription` | string | `''` | | The description of the Workspace to be created. | +| `workspaceFriendlyName` | string | `''` | | The friendly name of the Workspace to be created. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the AVD workspace. | +| `resourceGroupName` | string | The resource group the AVD workspace was deployed into. | +| `resourceId` | string | The resource ID of the AVD workspace. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.DesktopVirtualization/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + name: '<>-az-avdws-x-001' + // Non-required parameters + appGroupResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.DesktopVirtualization/applicationgroups/adp-<>-az-avdag-x-001' + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + location: 'westeurope' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + workspaceDescription: 'This is my first AVD Workspace' + workspaceFriendlyName: 'My first AVD Workspace' + } +} +``` + +
+

+ +

+ +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": "<>-az-avdws-x-001" + }, + // Non-required parameters + "appGroupResourceIds": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.DesktopVirtualization/applicationgroups/adp-<>-az-avdag-x-001" + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "location": { + "value": "westeurope" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "workspaceDescription": { + "value": "This is my first AVD Workspace" + }, + "workspaceFriendlyName": { + "value": "My first AVD Workspace" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DesktopVirtualization/workspaces/version.json b/modules/Microsoft.DesktopVirtualization/workspaces/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DesktopVirtualization/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..fa29f14 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'CosmosBackupOperator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db7b14f2-5adf-42da-9f96-f2ee17bab5cb') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + '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 databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2021-06-15' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(databaseAccount.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: databaseAccount +}] diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/.test/gremlindb.parameters.json b/modules/Microsoft.DocumentDB/databaseAccounts/.test/gremlindb.parameters.json new file mode 100644 index 0000000..ec02562 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/.test/gremlindb.parameters.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cdb-gremlindb-001" + }, + "location": { + "value": "West Europe" + }, + "locations": { + "value": [ + { + "locationName": "West Europe", + "failoverPriority": 0, + "isZoneRedundant": false + }, + { + "locationName": "North Europe", + "failoverPriority": 1, + "isZoneRedundant": false + } + ] + }, + "capabilitiesToAdd": { + "value": [ + "EnableGremlin" + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "gremlinDatabases": { + "value": [ + { + "name": "<>-az-gdb-x-001", + "graphs": [ + { + "name": "car_collection", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/car_id" + ] + }, + { + "name": "truck_collection", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/truck_id" + ] + } + ] + }, + { + "name": "<>-az-gdb-x-002", + "collections": [ + { + "name": "bike_collection", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/bike_id" + ] + }, + { + "name": "bicycle_collection", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/bicycle_id" + ] + } + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + } + } +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/.test/mongodb.parameters.json b/modules/Microsoft.DocumentDB/databaseAccounts/.test/mongodb.parameters.json new file mode 100644 index 0000000..c9c6465 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/.test/mongodb.parameters.json @@ -0,0 +1,240 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cdb-mongodb-001" + }, + "location": { + "value": "West Europe" + }, + "locations": { + "value": [ + { + "locationName": "West Europe", + "failoverPriority": 0, + "isZoneRedundant": false + }, + { + "locationName": "North Europe", + "failoverPriority": 1, + "isZoneRedundant": false + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "mongodbDatabases": { + "value": [ + { + "name": "<>-az-mdb-x-001", + "collections": [ + { + "name": "car_collection", + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "car_id", + "car_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "shardKey": { + "car_id": "Hash" + } + }, + { + "name": "truck_collection", + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "truck_id", + "truck_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "shardKey": { + "truck_id": "Hash" + } + } + ] + }, + { + "name": "<>-az-mdb-x-002", + "collections": [ + { + "name": "bike_collection", + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "bike_id", + "bike_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "shardKey": { + "bike_id": "Hash" + } + }, + { + "name": "bicycle_collection", + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "bicycle_id", + "bicycle_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "shardKey": { + "bicycle_id": "Hash" + } + } + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + } + } +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/.test/plain.parameters.json b/modules/Microsoft.DocumentDB/databaseAccounts/.test/plain.parameters.json new file mode 100644 index 0000000..564968c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/.test/plain.parameters.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cdb-plain-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "locations": { + "value": [ + { + "locationName": "West Europe", + "failoverPriority": 0, + "isZoneRedundant": false + }, + { + "locationName": "North Europe", + "failoverPriority": 1, + "isZoneRedundant": false + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/.test/sqldb.parameters.json b/modules/Microsoft.DocumentDB/databaseAccounts/.test/sqldb.parameters.json new file mode 100644 index 0000000..11f8386 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/.test/sqldb.parameters.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-cdb-sqldb-001" + }, + "location": { + "value": "West Europe" + }, + "locations": { + "value": [ + { + "locationName": "West Europe", + "failoverPriority": 0, + "isZoneRedundant": false + }, + { + "locationName": "North Europe", + "failoverPriority": 1, + "isZoneRedundant": false + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "sqlDatabases": { + "value": [ + { + "name": "<>-az-sql-x-001", + "containers": [ + { + "name": "container-001", + "paths": [ + "/myPartitionKey" + ], + "kind": "Hash" + } + ] + }, + { + "name": "<>-az-sql-x-002", + "containers": [] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/deploy.bicep new file mode 100644 index 0000000..aebd612 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/deploy.bicep @@ -0,0 +1,366 @@ +@description('Required. Name of the Database Account.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Database Account resource.') +param tags object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. The offer type for the Cosmos DB database account.') +@allowed([ + 'Standard' +]) +param databaseAccountOfferType string = 'Standard' + +@description('Required. Locations enabled for the Cosmos DB account.') +param locations array + +@allowed([ + 'Eventual' + 'ConsistentPrefix' + 'Session' + 'BoundedStaleness' + 'Strong' +]) +@description('Optional. The default consistency level of the Cosmos DB account.') +param defaultConsistencyLevel string = 'Session' + +@description('Optional. Enable automatic failover for regions.') +param automaticFailover bool = true + +@minValue(10) +@maxValue(2147483647) +@description('Optional. Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000.') +param maxStalenessPrefix int = 100000 + +@minValue(5) +@maxValue(86400) +@description('Optional. Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400.') +param maxIntervalInSeconds int = 300 + +@description('Optional. Specifies the MongoDB server version to use.') +@allowed([ + '3.2' + '3.6' + '4.0' + '4.2' +]) +param serverVersion string = '4.2' + +@description('Optional. SQL Databases configurations.') +param sqlDatabases array = [] + +@description('Optional. MongoDB Databases configurations.') +param mongodbDatabases array = [] + +@description('Optional. Gremlin Databases configurations.') +param gremlinDatabases array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalIds\' 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. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DataPlaneRequests' + 'MongoRequests' + 'QueryRuntimeStatistics' + 'PartitionKeyStatistics' + 'PartitionKeyRUConsumption' + 'ControlPlaneRequests' + 'CassandraRequests' + 'GremlinRequests' + 'TableApiRequests' +]) +param diagnosticLogCategoriesToEnable array = [ + 'DataPlaneRequests' + 'MongoRequests' + 'QueryRuntimeStatistics' + 'PartitionKeyStatistics' + 'PartitionKeyRUConsumption' + 'ControlPlaneRequests' + 'CassandraRequests' + 'GremlinRequests' + 'TableApiRequests' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Requests' +]) +param diagnosticMetricsToEnable array = [ + 'Requests' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@allowed([ + 'EnableCassandra' + 'EnableTable' + 'EnableGremlin' + 'EnableMongo' + 'DisableRateLimitingResponses' + 'EnableServerless' +]) +@description('Optional. List of Cosmos DB capabilities for the account.') +param capabilitiesToAdd array = [] + +@allowed([ + 'Periodic' + 'Continuous' +]) +@description('Optional. Describes the mode of backups.') +param backupPolicyType string = 'Continuous' + +@allowed([ + 'Continuous30Days' + 'Continuous7Days' +]) +@description('Optional. Configuration values for continuous mode backup.') +param backupPolicyContinuousTier string = 'Continuous30Days' + +@minValue(60) +@maxValue(1440) +@description('Optional. An integer representing the interval in minutes between two backups. Only applies to periodic backup type.') +param backupIntervalInMinutes int = 240 + +@minValue(2) +@maxValue(720) +@description('Optional. An integer representing the time (in hours) that each backup is retained. Only applies to periodic backup type.') +param backupRetentionIntervalInHours int = 8 + +@allowed([ + 'Geo' + 'Local' + 'Zone' +]) +@description('Optional. Enum to indicate type of backup residency. Only applies to periodic backup type.') +param backupStorageRedundancy string = 'Local' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var consistencyPolicy = { + Eventual: { + defaultConsistencyLevel: 'Eventual' + } + ConsistentPrefix: { + defaultConsistencyLevel: 'ConsistentPrefix' + } + Session: { + defaultConsistencyLevel: 'Session' + } + BoundedStaleness: { + defaultConsistencyLevel: 'BoundedStaleness' + maxStalenessPrefix: maxStalenessPrefix + maxIntervalInSeconds: maxIntervalInSeconds + } + Strong: { + defaultConsistencyLevel: 'Strong' + } +} + +var databaseAccount_locations = [for location in locations: { + failoverPriority: location.failoverPriority + isZoneRedundant: location.isZoneRedundant + locationName: location.locationName +}] + +var kind = !empty(sqlDatabases) || !empty(gremlinDatabases) ? 'GlobalDocumentDB' : (!empty(mongodbDatabases) ? 'MongoDB' : 'Parse') + +var enableReferencedModulesTelemetry = false + +var capabilities = [for capability in capabilitiesToAdd: { + name: capability +}] + +var backupPolicy = backupPolicyType == 'Continuous' ? { + type: backupPolicyType + continuousModeProperties: { + tier: backupPolicyContinuousTier + } +} : { + type: backupPolicyType + periodicModeProperties: { + backupIntervalInMinutes: backupIntervalInMinutes + backupRetentionIntervalInHours: backupRetentionIntervalInHours + backupStorageRedundancy: backupStorageRedundancy + } +} + +var databaseAccount_properties = union({ + databaseAccountOfferType: databaseAccountOfferType + }, ((!empty(sqlDatabases) || !empty(mongodbDatabases) || !empty(gremlinDatabases)) ? { + // Common properties + consistencyPolicy: consistencyPolicy[defaultConsistencyLevel] + locations: databaseAccount_locations + capabilities: capabilities + backupPolicy: backupPolicy + } : {}), (!empty(sqlDatabases) ? { + // SQLDB properties + enableAutomaticFailover: automaticFailover + } : {}), (!empty(mongodbDatabases) ? { + // MongoDb properties + apiProperties: { + serverVersion: serverVersion + } + } : {})) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2022-02-15-preview' = { + name: name + location: location + tags: tags + identity: identity + kind: kind + properties: databaseAccount_properties +} + +resource databaseAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${databaseAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: databaseAccount +} + +resource databaseAccount_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: databaseAccount +} + +module databaseAccount_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: databaseAccount.id + } +}] + +module databaseAccount_sqlDatabases 'sqlDatabases/deploy.bicep' = [for sqlDatabase in sqlDatabases: { + name: '${uniqueString(deployment().name, location)}-sqldb-${sqlDatabase.name}' + params: { + databaseAccountName: databaseAccount.name + name: sqlDatabase.name + containers: contains(sqlDatabase, 'containers') ? sqlDatabase.containers : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module databaseAccount_mongodbDatabases 'mongodbDatabases/deploy.bicep' = [for mongodbDatabase in mongodbDatabases: { + name: '${uniqueString(deployment().name, location)}-mongodb-${mongodbDatabase.name}' + params: { + databaseAccountName: databaseAccount.name + name: mongodbDatabase.name + collections: contains(mongodbDatabase, 'collections') ? mongodbDatabase.collections : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module databaseAccount_gremlinDatabases 'gremlinDatabases/deploy.bicep' = [for gremlinDatabase in gremlinDatabases: { + name: '${uniqueString(deployment().name, location)}-gremlin-${gremlinDatabase.name}' + params: { + databaseAccountName: databaseAccount.name + name: gremlinDatabase.name + graphs: contains(gremlinDatabase, 'graphs') ? gremlinDatabase.graphs : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the database account.') +output name string = databaseAccount.name + +@description('The resource ID of the database account.') +output resourceId string = databaseAccount.id + +@description('The name of the resource group the database account was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(databaseAccount.identity, 'principalId') ? databaseAccount.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = databaseAccount.location diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/deploy.bicep new file mode 100644 index 0000000..216c5dd --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/deploy.bicep @@ -0,0 +1,92 @@ +@description('Required. Name of the Gremlin database.') +param name string + +@description('Optional. Tags of the Gremlin database resource.') +param tags object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Conditional. The name of the parent Gremlin database. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Optional. Array of graphs to deploy in the Gremlin database.') +param graphs array = [] + +@description('Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored.') +param maxThroughput int = 4000 + +@description('Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`.') +param throughput int = -1 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2022-02-15-preview' existing = { + name: databaseAccountName +} + +var databaseOptions = contains(databaseAccount.properties.capabilities, { name: 'EnableServerless' }) ? {} : { + autoscaleSettings: throughput == -1 ? { + maxThroughput: maxThroughput + } : null + throughput: throughput != -1 ? throughput : null +} + +resource gremlinDatabase 'Microsoft.DocumentDB/databaseAccounts/gremlinDatabases@2022-02-15-preview' = { + name: name + tags: tags + parent: databaseAccount + identity: identity + properties: { + options: databaseOptions + resource: { + id: name + } + } +} + +module gremlinDatabase_gremlinGraphs 'graphs/deploy.bicep' = [for graph in graphs: { + name: '${uniqueString(deployment().name, gremlinDatabase.name)}-gremlindb-${graph.name}' + params: { + name: graph.name + gremlinDatabaseName: name + databaseAccountName: databaseAccountName + enableDefaultTelemetry: enableReferencedModulesTelemetry + automaticIndexing: contains(graph, 'automaticIndexing') ? graph.automaticIndexing : true + partitionKeyPaths: !empty(graph.partitionKeyPaths) ? graph.partitionKeyPaths : [] + } +}] + +@description('The name of the Gremlin database.') +output name string = gremlinDatabase.name + +@description('The resource ID of the Gremlin database.') +output resourceId string = gremlinDatabase.id + +@description('The name of the resource group the Gremlin database was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/deploy.bicep new file mode 100644 index 0000000..59742e7 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/deploy.bicep @@ -0,0 +1,66 @@ +@description('Required. Name of the graph.') +param name string + +@description('Optional. Tags of the Gremlin graph resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Conditional. The name of the parent Gremlin Database. Required if the template is used in a standalone deployment.') +param gremlinDatabaseName string + +@description('Optional. Indicates if the indexing policy is automatic.') +param automaticIndexing bool = true + +@description('Optional. List of paths using which data within the container can be partitioned.') +param partitionKeyPaths array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2022-02-15-preview' existing = { + name: databaseAccountName + + resource gremlinDatabase 'gremlinDatabases@2022-02-15-preview' existing = { + name: gremlinDatabaseName + } +} + +resource gremlinGraph 'Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs@2022-02-15-preview' = { + name: name + tags: tags + parent: databaseAccount::gremlinDatabase + properties: { + resource: { + id: name + indexingPolicy: { + automatic: automaticIndexing + } + partitionKey: { + paths: !empty(partitionKeyPaths) ? partitionKeyPaths : null + } + } + } +} + +@description('The name of the graph.') +output name string = gremlinGraph.name + +@description('The resource ID of the graph.') +output resourceId string = gremlinGraph.id + +@description('The name of the resource group the graph was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/readme.md new file mode 100644 index 0000000..3ae7440 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/readme.md @@ -0,0 +1,122 @@ +# DocumentDB DatabaseAccounts GremlinDatabases Graphs `[Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs]` + +This module deploys DocumentDB DatabaseAccounts GremlinDatabases Graphs. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts/gremlinDatabases/graphs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the graph. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | +| `gremlinDatabaseName` | string | The name of the parent Gremlin Database. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `automaticIndexing` | bool | `True` | Indicates if the indexing policy is automatic. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `partitionKeyPaths` | array | `[]` | List of paths using which data within the container can be partitioned. | +| `tags` | object | `{object}` | Tags of the Gremlin graph resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `partitionKeyPaths`, `uniqueKeyPaths` + +Different kinds of paths can be provided as array of strings: + +

+ +Bicep format + +```bicep +graphs: [ + { + name: 'graph01' + automaticIndexing: true + partitionKeyPaths: [ + '/name' + ], + + } + { + name: 'graph02' + automaticIndexing: true + partitionKeyPaths: [ + '/address' + ] + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the graph. | +| `resourceGroupName` | string | The name of the resource group the graph was created in. | +| `resourceId` | string | The resource ID of the graph. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/readme.md new file mode 100644 index 0000000..525c7cb --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/readme.md @@ -0,0 +1,183 @@ +# DocumentDB DatabaseAccounts GremlinDatabases `[Microsoft.DocumentDB/databaseAccounts/gremlinDatabases]` + +This module deploys a GremlinDB within a CosmosDB account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/gremlinDatabases` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts/gremlinDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts/gremlinDatabases/graphs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Gremlin database. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Gremlin database. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `graphs` | _[graphs](graphs/readme.md)_ array | `[]` | Array of graphs to deploy in the Gremlin database. | +| `maxThroughput` | int | `4000` | Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored. | +| `systemAssignedIdentity` | bool | `False` | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | Tags of the Gremlin database resource. | +| `throughput` | int | `-1` | Request Units per second (for example 10000). Cannot be set together with `maxThroughput`. | +| `userAssignedIdentities` | object | `{object}` | The ID(s) to assign to the resource. | + + +### Parameter Usage: `graphs` + +List of graph databaseAccounts + +

+ +Parameter JSON format + +```json +"graphs": { + "value": [ + { + "name": "graph01", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/name" + ] + }, + { + "name": "graph02", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/name" + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +graphs: [ + { + name: 'graph01' + automaticIndexing: true + partitionKeyPaths: [ + '/name' + ] + } + { + name: 'graph02' + automaticIndexing: true + partitionKeyPaths: [ + '/name' + ] + } +] +``` + +
+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +
+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Gremlin database. | +| `resourceGroupName` | string | The name of the resource group the Gremlin database was created in. | +| `resourceId` | string | The resource ID of the Gremlin database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/deploy.bicep new file mode 100644 index 0000000..a64540e --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/deploy.bicep @@ -0,0 +1,64 @@ +@description('Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Conditional. The name of the parent mongodb database. Required if the template is used in a standalone deployment.') +param mongodbDatabaseName string + +@description('Required. Name of the collection.') +param name string + +@description('Optional. Name of the mongodb database.') +param throughput int = 400 + +@description('Required. Indexes for the collection.') +param indexes array + +@description('Required. ShardKey for the collection.') +param shardKey object + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' existing = { + name: databaseAccountName + + resource mongodbDatabase 'mongodbDatabases@2021-07-01-preview' existing = { + name: mongodbDatabaseName + } +} + +resource collection 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections@2021-07-01-preview' = { + name: name + parent: databaseAccount::mongodbDatabase + properties: { + options: contains(databaseAccount.properties.capabilities, { name: 'EnableServerless' }) ? null : { + throughput: throughput + } + resource: { + id: name + indexes: indexes + shardKey: shardKey + } + } +} + +@description('The name of the mongodb database.') +output name string = collection.name + +@description('The resource ID of the mongodb database.') +output resourceId string = collection.id + +@description('The name of the resource group the mongodb database was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/readme.md new file mode 100644 index 0000000..e0305d7 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/readme.md @@ -0,0 +1,180 @@ +# DocumentDB Database Account MongoDB databases Collections `[Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections]` + +This module deploys a collection within a MongoDB. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/mongodbDatabases/collections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `indexes` | array | Indexes for the collection. | +| `name` | string | Name of the collection. | +| `shardKey` | object | ShardKey for the collection. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment. | +| `mongodbDatabaseName` | string | The name of the parent mongodb database. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `throughput` | int | `400` | Name of the mongodb database. | + + +### Parameter Usage: `indexes` + +Array of index keys as MongoIndex. The array contains keys for each MongoDB collection in the Azure Cosmos DB service with a collection resource object (as `key`) and collection index options (as `options`). + +

+ +Parameter JSON format + +```json +"indexes": { + "value": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "estate_id", + "estate_address" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +indexes: [ + { + key: { + keys: [ + '_id' + ] + } + } + { + key: { + keys: [ + '$**' + ] + } + } + { + key: { + keys: [ + 'estate_id' + 'estate_address' + ] + } + options: { + unique: true + } + } + { + key: { + keys: [ + '_ts' + ] + } + options: { + expireAfterSeconds: 2629746 + } + } +] +``` + +
+

+ +### Parameter Usage: `shardKey` + +The shard key and partition kind pair, only support "Hash" partition kind. + +

+ +Parameter JSON format + +```json +"shardKey": { + "value": { + "estate_id": "Hash" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +shardKey: { + estate_id: 'Hash' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the mongodb database. | +| `resourceGroupName` | string | The name of the resource group the mongodb database was created in. | +| `resourceId` | string | The resource ID of the mongodb database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/deploy.bicep new file mode 100644 index 0000000..235b51f --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/deploy.bicep @@ -0,0 +1,70 @@ +@description('Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Required. Name of the mongodb database.') +param name string + +@description('Optional. Name of the mongodb database.') +param throughput int = 400 + +@description('Optional. Collections in the mongodb database.') +param collections array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' existing = { + name: databaseAccountName +} + +resource mongodbDatabase 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2021-07-01-preview' = { + name: name + parent: databaseAccount + tags: tags + properties: { + resource: { + id: name + } + options: contains(databaseAccount.properties.capabilities, { name: 'EnableServerless' }) ? null : { + throughput: throughput + } + } +} + +module mongodbDatabase_collections 'collections/deploy.bicep' = [for collection in collections: { + name: '${uniqueString(deployment().name, mongodbDatabase.name)}-collection-${collection.name}' + params: { + databaseAccountName: databaseAccountName + mongodbDatabaseName: name + name: collection.name + indexes: collection.indexes + shardKey: collection.shardKey + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the mongodb database.') +output name string = mongodbDatabase.name + +@description('The resource ID of the mongodb database.') +output resourceId string = mongodbDatabase.id + +@description('The name of the resource group the mongodb database was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/readme.md new file mode 100644 index 0000000..8235b62 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/readme.md @@ -0,0 +1,95 @@ +# DocumentDB Database Account MongoDB databases `[Microsoft.DocumentDB/databaseAccounts/mongodbDatabases]` + +This module deploys a MongoDB within a CosmosDB account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/mongodbDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/mongodbDatabases/collections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the mongodb database. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `collections` | _[collections](collections/readme.md)_ array | `[]` | Collections in the mongodb database. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `tags` | object | `{object}` | Tags of the resource. | +| `throughput` | int | `400` | Name of the mongodb database. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `collections` + +Please reference the documentation for [collections](./collections/readme.md) + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the mongodb database. | +| `resourceGroupName` | string | The name of the resource group the mongodb database was created in. | +| `resourceId` | string | The resource ID of the mongodb database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/readme.md new file mode 100644 index 0000000..7c9c13b --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/readme.md @@ -0,0 +1,1493 @@ +# DocumentDB Database Accounts `[Microsoft.DocumentDB/databaseAccounts]` + +This module deploys a DocumentDB database account and its child resources. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DocumentDB/databaseAccounts` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts) | +| `Microsoft.DocumentDB/databaseAccounts/gremlinDatabases` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts/gremlinDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs` | [2022-02-15-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2022-02-15-preview/databaseAccounts/gremlinDatabases/graphs) | +| `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/mongodbDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/mongodbDatabases/collections) | +| `Microsoft.DocumentDB/databaseAccounts/sqlDatabases` | [2021-06-15](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-06-15/databaseAccounts/sqlDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/sqlDatabases/containers) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `locations` | array | Locations enabled for the Cosmos DB account. | +| `name` | string | Name of the Database Account. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `automaticFailover` | bool | `True` | | Enable automatic failover for regions. | +| `backupIntervalInMinutes` | int | `240` | | An integer representing the interval in minutes between two backups. Only applies to periodic backup type. | +| `backupPolicyContinuousTier` | string | `'Continuous30Days'` | `[Continuous30Days, Continuous7Days]` | Configuration values for continuous mode backup. | +| `backupPolicyType` | string | `'Continuous'` | `[Continuous, Periodic]` | Describes the mode of backups. | +| `backupRetentionIntervalInHours` | int | `8` | | An integer representing the time (in hours) that each backup is retained. Only applies to periodic backup type. | +| `backupStorageRedundancy` | string | `'Local'` | `[Geo, Local, Zone]` | Enum to indicate type of backup residency. Only applies to periodic backup type. | +| `capabilitiesToAdd` | array | `[]` | `[DisableRateLimitingResponses, EnableCassandra, EnableGremlin, EnableMongo, EnableServerless, EnableTable]` | List of Cosmos DB capabilities for the account. | +| `databaseAccountOfferType` | string | `'Standard'` | `[Standard]` | The offer type for the Cosmos DB database account. | +| `defaultConsistencyLevel` | string | `'Session'` | `[BoundedStaleness, ConsistentPrefix, Eventual, Session, Strong]` | The default consistency level of the Cosmos DB account. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[CassandraRequests, ControlPlaneRequests, DataPlaneRequests, GremlinRequests, MongoRequests, PartitionKeyRUConsumption, PartitionKeyStatistics, QueryRuntimeStatistics, TableApiRequests]` | `[CassandraRequests, ControlPlaneRequests, DataPlaneRequests, GremlinRequests, MongoRequests, PartitionKeyRUConsumption, PartitionKeyStatistics, QueryRuntimeStatistics, TableApiRequests]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Requests]` | `[Requests]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `gremlinDatabases` | _[gremlinDatabases](gremlinDatabases/readme.md)_ array | `[]` | | Gremlin Databases configurations. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxIntervalInSeconds` | int | `300` | | Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400. | +| `maxStalenessPrefix` | int | `100000` | | Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000. | +| `mongodbDatabases` | _[mongodbDatabases](mongodbDatabases/readme.md)_ array | `[]` | | MongoDB Databases configurations. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' 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'. | +| `serverVersion` | string | `'4.2'` | `[3.2, 3.6, 4.0, 4.2]` | Specifies the MongoDB server version to use. | +| `sqlDatabases` | _[sqlDatabases](sqlDatabases/readme.md)_ array | `[]` | | SQL Databases configurations. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Database Account resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `locations` + +

+ +Parameter JSON format + +```json +"locations": { + "value": [ + { + "failoverPriority": 1, + "locationName": "East US", + "isZoneRedundant": false + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +locations: [ + { + failoverPriority: 1 + locationName: 'East US' + isZoneRedundant: false + } +] +``` + +
+

+ +### Parameter Usage: `sqlDatabases` + +

+ +Parameter JSON format + +```json +"sqlDatabases": { + "value": [ + { + "name": "sxx-az-sql-x-001", + "containers": [ + "container-001", + "container-002" + ] + }, + { + "name": "sxx-az-sql-x-002", + "containers": [] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +sqlDatabases: { + value: [ + { + name: 'sxx-az-sql-x-001' + containers: [ + 'container-001' + 'container-002' + ] + } + { + name: 'sxx-az-sql-x-002' + containers: [] + } + ] +} +``` + +
+

+ +### Parameter Usage: `mongodbDatabases` + +

+ +Parameter JSON format + +```json +"mongodbDatabases": { + "value": [ + { + "name": "sxx-az-mdb-x-001", + "collections": [ + <...> + ] + }, + { + "name": "sxx-az-mdb-x-002", + "collections": [ + <...> + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +mongodbDatabases: [ + { + name: 'sxx-az-mdb-x-001' + collections: [ + <...> + ] + } + { + name: 'sxx-az-mdb-x-002' + collections: [ + <...> + ] + } +] +``` + +
+

+ +Please reference the documentation for [mongodbDatabases](./mongodbDatabases/readme.md) + +### Parameter Usage: `gremlinDatabases` + +

+ +Parameter JSON format + +```json +"mongodbDatabases": { + "value": [ + { + "name": "graphDb01", + "graphs": [ + { + "name": "graph01", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/name" + ] + }, + { + "name": "graph02", + "automaticIndexing": true, + "partitionKeyPaths": [ + "/name" + ] + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +gremlinDatabases: [ + { + name: 'graphDb01' + graphs: [ + { + name: 'graph01' + automaticIndexing: true + partitionKeyPaths: [ + '/name' + ] + } + { + name: 'graph02' + automaticIndexing: true + partitionKeyPaths: [ + '/name' + ] + } + ] + } +] +``` + +
+

+ +Please reference the documentation for [gremlinDatabases](./gremlinDatabases/readme.md) + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to 'ServicePrincipal'. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Desktop Virtualization User", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Desktop Virtualization User' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `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. | +| `resourceId` | string | The resource ID of the database account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Gremlindb

+ +
+ +via Bicep module + +```bicep +module databaseAccounts './Microsoft.DocumentDB/databaseAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DatabaseAccounts' + params: { + // Required parameters + locations: [ + { + failoverPriority: 0 + isZoneRedundant: false + locationName: 'West Europe' + } + { + failoverPriority: 1 + isZoneRedundant: false + locationName: 'North Europe' + } + ] + name: '<>-az-cdb-gremlindb-001' + // Non-required parameters + capabilitiesToAdd: [ + 'EnableGremlin' + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + gremlinDatabases: [ + { + graphs: [ + { + automaticIndexing: true + name: 'car_collection' + partitionKeyPaths: [ + '/car_id' + ] + } + { + automaticIndexing: true + name: 'truck_collection' + partitionKeyPaths: [ + '/truck_id' + ] + } + ] + name: '<>-az-gdb-x-001' + } + { + collections: [ + { + automaticIndexing: true + name: 'bike_collection' + partitionKeyPaths: [ + '/bike_id' + ] + } + { + automaticIndexing: true + name: 'bicycle_collection' + partitionKeyPaths: [ + '/bicycle_id' + ] + } + ] + name: '<>-az-gdb-x-002' + } + ] + location: 'West Europe' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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 + "locations": { + "value": [ + { + "failoverPriority": 0, + "isZoneRedundant": false, + "locationName": "West Europe" + }, + { + "failoverPriority": 1, + "isZoneRedundant": false, + "locationName": "North Europe" + } + ] + }, + "name": { + "value": "<>-az-cdb-gremlindb-001" + }, + // Non-required parameters + "capabilitiesToAdd": { + "value": [ + "EnableGremlin" + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "gremlinDatabases": { + "value": [ + { + "graphs": [ + { + "automaticIndexing": true, + "name": "car_collection", + "partitionKeyPaths": [ + "/car_id" + ] + }, + { + "automaticIndexing": true, + "name": "truck_collection", + "partitionKeyPaths": [ + "/truck_id" + ] + } + ], + "name": "<>-az-gdb-x-001" + }, + { + "collections": [ + { + "automaticIndexing": true, + "name": "bike_collection", + "partitionKeyPaths": [ + "/bike_id" + ] + }, + { + "automaticIndexing": true, + "name": "bicycle_collection", + "partitionKeyPaths": [ + "/bicycle_id" + ] + } + ], + "name": "<>-az-gdb-x-002" + } + ] + }, + "location": { + "value": "West Europe" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

+ +

Example 2: Mongodb

+ +
+ +via Bicep module + +```bicep +module databaseAccounts './Microsoft.DocumentDB/databaseAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DatabaseAccounts' + params: { + // Required parameters + locations: [ + { + failoverPriority: 0 + isZoneRedundant: false + locationName: 'West Europe' + } + { + failoverPriority: 1 + isZoneRedundant: false + locationName: 'North Europe' + } + ] + name: '<>-az-cdb-mongodb-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + location: 'West Europe' + mongodbDatabases: [ + { + collections: [ + { + indexes: [ + { + key: { + keys: [ + '_id' + ] + } + } + { + key: { + keys: [ + '$**' + ] + } + } + { + key: { + keys: [ + 'car_id' + 'car_model' + ] + } + options: { + unique: true + } + } + { + key: { + keys: [ + '_ts' + ] + } + options: { + expireAfterSeconds: 2629746 + } + } + ] + name: 'car_collection' + shardKey: { + car_id: 'Hash' + } + } + { + indexes: [ + { + key: { + keys: [ + '_id' + ] + } + } + { + key: { + keys: [ + '$**' + ] + } + } + { + key: { + keys: [ + 'truck_id' + 'truck_model' + ] + } + options: { + unique: true + } + } + { + key: { + keys: [ + '_ts' + ] + } + options: { + expireAfterSeconds: 2629746 + } + } + ] + name: 'truck_collection' + shardKey: { + truck_id: 'Hash' + } + } + ] + name: '<>-az-mdb-x-001' + } + { + collections: [ + { + indexes: [ + { + key: { + keys: [ + '_id' + ] + } + } + { + key: { + keys: [ + '$**' + ] + } + } + { + key: { + keys: [ + 'bike_id' + 'bike_model' + ] + } + options: { + unique: true + } + } + { + key: { + keys: [ + '_ts' + ] + } + options: { + expireAfterSeconds: 2629746 + } + } + ] + name: 'bike_collection' + shardKey: { + bike_id: 'Hash' + } + } + { + indexes: [ + { + key: { + keys: [ + '_id' + ] + } + } + { + key: { + keys: [ + '$**' + ] + } + } + { + key: { + keys: [ + 'bicycle_id' + 'bicycle_model' + ] + } + options: { + unique: true + } + } + { + key: { + keys: [ + '_ts' + ] + } + options: { + expireAfterSeconds: 2629746 + } + } + ] + name: 'bicycle_collection' + shardKey: { + bicycle_id: 'Hash' + } + } + ] + name: '<>-az-mdb-x-002' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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 + "locations": { + "value": [ + { + "failoverPriority": 0, + "isZoneRedundant": false, + "locationName": "West Europe" + }, + { + "failoverPriority": 1, + "isZoneRedundant": false, + "locationName": "North Europe" + } + ] + }, + "name": { + "value": "<>-az-cdb-mongodb-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "location": { + "value": "West Europe" + }, + "mongodbDatabases": { + "value": [ + { + "collections": [ + { + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "car_id", + "car_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "name": "car_collection", + "shardKey": { + "car_id": "Hash" + } + }, + { + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "truck_id", + "truck_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "name": "truck_collection", + "shardKey": { + "truck_id": "Hash" + } + } + ], + "name": "<>-az-mdb-x-001" + }, + { + "collections": [ + { + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "bike_id", + "bike_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "name": "bike_collection", + "shardKey": { + "bike_id": "Hash" + } + }, + { + "indexes": [ + { + "key": { + "keys": [ + "_id" + ] + } + }, + { + "key": { + "keys": [ + "$**" + ] + } + }, + { + "key": { + "keys": [ + "bicycle_id", + "bicycle_model" + ] + }, + "options": { + "unique": true + } + }, + { + "key": { + "keys": [ + "_ts" + ] + }, + "options": { + "expireAfterSeconds": 2629746 + } + } + ], + "name": "bicycle_collection", + "shardKey": { + "bicycle_id": "Hash" + } + } + ], + "name": "<>-az-mdb-x-002" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

+ +

Example 3: Plain

+ +
+ +via Bicep module + +```bicep +module databaseAccounts './Microsoft.DocumentDB/databaseAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DatabaseAccounts' + params: { + // Required parameters + locations: [ + { + failoverPriority: 0 + isZoneRedundant: false + locationName: 'West Europe' + } + { + failoverPriority: 1 + isZoneRedundant: false + locationName: 'North Europe' + } + ] + name: '<>-az-cdb-plain-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "locations": { + "value": [ + { + "failoverPriority": 0, + "isZoneRedundant": false, + "locationName": "West Europe" + }, + { + "failoverPriority": 1, + "isZoneRedundant": false, + "locationName": "North Europe" + } + ] + }, + "name": { + "value": "<>-az-cdb-plain-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 4: Sqldb

+ +
+ +via Bicep module + +```bicep +module databaseAccounts './Microsoft.DocumentDB/databaseAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DatabaseAccounts' + params: { + // Required parameters + locations: [ + { + failoverPriority: 0 + isZoneRedundant: false + locationName: 'West Europe' + } + { + failoverPriority: 1 + isZoneRedundant: false + locationName: 'North Europe' + } + ] + name: '<>-az-cdb-sqldb-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + location: 'West Europe' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sqlDatabases: [ + { + containers: [ + { + kind: 'Hash' + name: 'container-001' + paths: [ + '/myPartitionKey' + ] + } + ] + name: '<>-az-sql-x-001' + } + { + containers: [] + name: '<>-az-sql-x-002' + } + ] + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "locations": { + "value": [ + { + "failoverPriority": 0, + "isZoneRedundant": false, + "locationName": "West Europe" + }, + { + "failoverPriority": 1, + "isZoneRedundant": false, + "locationName": "North Europe" + } + ] + }, + "name": { + "value": "<>-az-cdb-sqldb-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "location": { + "value": "West Europe" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sqlDatabases": { + "value": [ + { + "containers": [ + { + "kind": "Hash", + "name": "container-001", + "paths": [ + "/myPartitionKey" + ] + } + ], + "name": "<>-az-sql-x-001" + }, + { + "containers": [], + "name": "<>-az-sql-x-002" + } + ] + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/deploy.bicep new file mode 100644 index 0000000..f832a93 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/deploy.bicep @@ -0,0 +1,75 @@ +@description('Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment.') +param databaseAccountName string + +@description('Conditional. The name of the parent SQL Database. Required if the template is used in a standalone deployment.') +param sqlDatabaseName string + +@description('Required. Name of the container.') +param name string + +@description('Optional. Request Units per second.') +param throughput int = 400 + +@description('Optional. Tags of the SQL Database resource.') +param tags object = {} + +@description('Optional. List of paths using which data within the container can be partitioned.') +param paths array = [] + +@description('Optional. Indicates the kind of algorithm used for partitioning.') +@allowed([ + 'Hash' + 'MultiHash' + 'Range' +]) +param kind string = 'Hash' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' existing = { + name: databaseAccountName + + resource sqlDatabase 'sqlDatabases@2021-07-01-preview' existing = { + name: sqlDatabaseName + } +} + +resource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-07-01-preview' = { + name: name + parent: databaseAccount::sqlDatabase + tags: tags + properties: { + resource: { + id: name + partitionKey: { + paths: paths + kind: kind + } + } + options: contains(databaseAccount.properties.capabilities, { name: 'EnableServerless' }) ? null : { + throughput: throughput + } + } +} + +@description('The name of the container.') +output name string = container.name + +@description('The resource ID of the container.') +output resourceId string = container.id + +@description('The name of the resource group the container was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/readme.md new file mode 100644 index 0000000..7d83d25 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/readme.md @@ -0,0 +1,90 @@ +# DocumentDB Database Account SQL Databases Containers `[Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers]` + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/sqlDatabases/containers) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the container. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | +| `sqlDatabaseName` | string | The name of the parent SQL Database. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `kind` | string | `'Hash'` | `[Hash, MultiHash, Range]` | Indicates the kind of algorithm used for partitioning. | +| `paths` | array | `[]` | | List of paths using which data within the container can be partitioned. | +| `tags` | object | `{object}` | | Tags of the SQL Database resource. | +| `throughput` | int | `400` | | Request Units per second. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the container. | +| `resourceGroupName` | string | The name of the resource group the container was created in. | +| `resourceId` | string | The resource ID of the container. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/deploy.bicep b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/deploy.bicep new file mode 100644 index 0000000..e0e8fff --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/deploy.bicep @@ -0,0 +1,70 @@ +@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 database .') +param name string + +@description('Optional. Array of containers to deploy in the SQL database.') +param containers array = [] + +@description('Optional. Request units per second.') +param throughput int = 400 + +@description('Optional. Tags of the SQL database resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2021-07-01-preview' existing = { + name: databaseAccountName +} + +resource sqlDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-06-15' = { + name: name + parent: databaseAccount + tags: tags + properties: { + resource: { + id: name + } + options: contains(databaseAccount.properties.capabilities, { name: 'EnableServerless' }) ? null : { + throughput: throughput + } + } +} + +module container 'containers/deploy.bicep' = [for container in containers: { + name: '${uniqueString(deployment().name, sqlDatabase.name)}-sqldb-${container.name}' + params: { + databaseAccountName: databaseAccountName + sqlDatabaseName: name + name: container.name + paths: container.paths + kind: container.kind + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the SQL database.') +output name string = sqlDatabase.name + +@description('The resource ID of the SQL database.') +output resourceId string = sqlDatabase.id + +@description('The name of the resource group the SQL database was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/readme.md b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/readme.md new file mode 100644 index 0000000..e408338 --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/readme.md @@ -0,0 +1,89 @@ +# DocumentDB Database Account SQL Databases `[Microsoft.DocumentDB/databaseAccounts/sqlDatabases]` + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/databaseAccounts/sqlDatabases` | [2021-06-15](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-06-15/databaseAccounts/sqlDatabases) | +| `Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2021-07-01-preview/databaseAccounts/sqlDatabases/containers) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the SQL database . | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseAccountName` | string | The name of the parent Database Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `containers` | _[containers](containers/readme.md)_ array | `[]` | Array of containers to deploy in the SQL database. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `tags` | object | `{object}` | Tags of the SQL database resource. | +| `throughput` | int | `400` | Request units per second. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | 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_ diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/sqlDatabases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.DocumentDB/databaseAccounts/version.json b/modules/Microsoft.DocumentDB/databaseAccounts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.DocumentDB/databaseAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventGrid/systemTopics/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.EventGrid/systemTopics/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d472103 --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7') + '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 systemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(systemTopic.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: systemTopic +}] diff --git a/modules/Microsoft.EventGrid/systemTopics/.test/min.parameters.json b/modules/Microsoft.EventGrid/systemTopics/.test/min.parameters.json new file mode 100644 index 0000000..87be2f3 --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/.test/min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-egstn-x-002" + }, + "source": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "topicType": { + "value": "Microsoft.Storage.StorageAccounts" + } + } +} diff --git a/modules/Microsoft.EventGrid/systemTopics/.test/parameters.json b/modules/Microsoft.EventGrid/systemTopics/.test/parameters.json new file mode 100644 index 0000000..5a415c7 --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/.test/parameters.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-egstn-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "source": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "topicType": { + "value": "Microsoft.Storage.StorageAccounts" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.EventGrid/systemTopics/deploy.bicep b/modules/Microsoft.EventGrid/systemTopics/deploy.bicep new file mode 100644 index 0000000..cd80306 --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/deploy.bicep @@ -0,0 +1,169 @@ +@description('Required. The name of the Event Grid Topic.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Required. Source for the system topic.') +param source string + +@description('Required. TopicType for the system topic.') +param topicType string + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DeliveryFailures' +]) +param diagnosticLogCategoriesToEnable array = [ + 'DeliveryFailures' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource systemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { + name: name + location: location + identity: identity + tags: tags + properties: { + source: source + topicType: topicType + } +} + +resource systemTopic_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${systemTopic.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: systemTopic +} + +resource systemTopic_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: systemTopic +} + +module systemTopic_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-EventGrid-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: systemTopic.id + } +}] + +@description('The name of the event grid system topic.') +output name string = systemTopic.name + +@description('The resource ID of the event grid system topic.') +output resourceId string = systemTopic.id + +@description('The name of the resource group the event grid system topic was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(systemTopic.identity, 'principalId') ? systemTopic.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = systemTopic.location diff --git a/modules/Microsoft.EventGrid/systemTopics/readme.md b/modules/Microsoft.EventGrid/systemTopics/readme.md new file mode 100644 index 0000000..2dc595c --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/readme.md @@ -0,0 +1,418 @@ +# Event Grid System Topics `[Microsoft.EventGrid/systemTopics]` + +This module deploys an Event Grid System Topic. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.EventGrid/systemTopics` | [2021-12-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2021-12-01/systemTopics) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Event Grid Topic. | +| `source` | string | Source for the system topic. | +| `topicType` | string | TopicType for the system topic. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[DeliveryFailures]` | `[DeliveryFailures]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to 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. + +

+ +Parameter JSON format + +```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": "<>", // e.g. vault, registry, file, blob, queue, table etc. + "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": "<>" // e.g. vault, registry, file, blob, queue, table etc. + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '<>' // e.g. vault registry file blob queue table etc. + 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' + ] + // Optional + customDnsConfigs: [ + { + 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: '<>' // e.g. vault registry file blob queue table etc. + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the event grid system topic. | +| `resourceGroupName` | string | The name of the resource group the event grid system topic was deployed into. | +| `resourceId` | string | The resource ID of the event grid system topic. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module systemTopics './Microsoft.EventGrid/systemTopics/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-SystemTopics' + params: { + // Required parameters + name: '<>-az-egstn-x-002' + source: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + topicType: 'Microsoft.Storage.StorageAccounts' + } +} +``` + +
+

+ +

+ +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": "<>-az-egstn-x-002" + }, + "source": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "topicType": { + "value": "Microsoft.Storage.StorageAccounts" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module systemTopics './Microsoft.EventGrid/systemTopics/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-SystemTopics' + params: { + // Required parameters + name: '<>-az-egstn-x-001' + source: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + topicType: 'Microsoft.Storage.StorageAccounts' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-egstn-x-001" + }, + "source": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "topicType": { + "value": "Microsoft.Storage.StorageAccounts" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.EventGrid/systemTopics/version.json b/modules/Microsoft.EventGrid/systemTopics/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventGrid/systemTopics/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventGrid/topics/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.EventGrid/topics/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a13e4f5 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7') + '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 eventGrid 'Microsoft.EventGrid/topics@2020-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(eventGrid.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: eventGrid +}] diff --git a/modules/Microsoft.EventGrid/topics/.test/min.parameters.json b/modules/Microsoft.EventGrid/topics/.test/min.parameters.json new file mode 100644 index 0000000..5e75832 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/.test/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": "<>-az-egtn-min-001" + } + } +} diff --git a/modules/Microsoft.EventGrid/topics/.test/parameters.json b/modules/Microsoft.EventGrid/topics/.test/parameters.json new file mode 100644 index 0000000..034bbc1 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/.test/parameters.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-egtn-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "inboundIpRules": { + "value": [ + { + "action": "Allow", + "ipMask": "40.74.28.0/23" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "topic", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net" + ] + } + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.EventGrid/topics/.test/pe.parameters.json b/modules/Microsoft.EventGrid/topics/.test/pe.parameters.json new file mode 100644 index 0000000..539c696 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/.test/pe.parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-egtn-pe-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "topic", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.EventGrid/topics/deploy.bicep b/modules/Microsoft.EventGrid/topics/deploy.bicep new file mode 100644 index 0000000..37f7b17 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/deploy.bicep @@ -0,0 +1,184 @@ +@description('Required. The name of the Event Grid Topic.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and inboundIpRules are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. This can be used to restrict traffic from specific IPs instead of all IPs. Note: These are considered only if PublicNetworkAccess is enabled.') +param inboundIpRules array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DeliveryFailures' + 'PublishFailures' +]) +param diagnosticLogCategoriesToEnable array = [ + 'DeliveryFailures' + 'PublishFailures' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource topic 'Microsoft.EventGrid/topics@2020-06-01' = { + name: name + location: location + tags: tags + properties: { + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(inboundIpRules) ? 'Disabled' : null) + inboundIpRules: (empty(inboundIpRules) ? null : inboundIpRules) + } +} + +resource topic_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${topic.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: topic +} + +resource topic_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: topic +} + +module topic_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-Topic-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(topic.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: topic.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module topic_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-topic-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: topic.id + } +}] + +@description('The name of the event grid topic.') +output name string = topic.name + +@description('The resource ID of the event grid topic.') +output resourceId string = topic.id + +@description('The name of the resource group the event grid topic was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = topic.location diff --git a/modules/Microsoft.EventGrid/topics/readme.md b/modules/Microsoft.EventGrid/topics/readme.md new file mode 100644 index 0000000..1e1514a --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/readme.md @@ -0,0 +1,478 @@ +# Event Grid Topics `[Microsoft.EventGrid/topics]` + +This module deploys an event grid topic. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.EventGrid/topics` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2020-06-01/topics) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Event Grid Topic. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[DeliveryFailures, PublishFailures]` | `[DeliveryFailures, PublishFailures]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `inboundIpRules` | array | `[]` | | This can be used to restrict traffic from specific IPs instead of all IPs. Note: These are considered only if PublicNetworkAccess is enabled. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and inboundIpRules are not set. | +| `roleAssignments` | array | `[]` | | 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}` | | 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the event grid topic. | +| `resourceGroupName` | string | The name of the resource group the event grid topic was deployed into. | +| `resourceId` | string | The resource ID of the event grid topic. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module topics './Microsoft.EventGrid/topics/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Topics' + params: { + name: '<>-az-egtn-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-egtn-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module topics './Microsoft.EventGrid/topics/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Topics' + params: { + // Required parameters + name: '<>-az-egtn-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + inboundIpRules: [ + { + action: 'Allow' + ipMask: '40.74.28.0/23' + } + ] + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net' + ] + } + service: 'topic' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-egtn-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "inboundIpRules": { + "value": [ + { + "action": "Allow", + "ipMask": "40.74.28.0/23" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net" + ] + }, + "service": "topic", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module topics './Microsoft.EventGrid/topics/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Topics' + params: { + // Required parameters + name: '<>-az-egtn-pe-001' + // Non-required parameters + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net' + ] + } + service: 'topic' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-egtn-pe-001" + }, + // Non-required parameters + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.eventgrid.azure.net" + ] + }, + "service": "topic", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.EventGrid/topics/version.json b/modules/Microsoft.EventGrid/topics/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.EventGrid/topics/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.EventHub/namespaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.EventHub/namespaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..3c44567 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,73 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Event Hubs Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f526a384-b230-433a-b45c-95f59c4a2dec') + 'Azure Event Hubs Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a638d3c7-ab3a-418d-83e6-5f17a39d4fde') + 'Azure Event Hubs Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2b629674-e913-4c01-ae53-ef4638d8f975') + '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') + 'Schema Registry Contributor (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5dffeca3-4936-4216-b2bc-10343a5abb25') + 'Schema Registry Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2c56ea50-c6b3-40a6-83c0-9d98858bc7d2') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource eventHubNamespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(eventHubNamespace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: eventHubNamespace +}] diff --git a/modules/Microsoft.EventHub/namespaces/.test/min.parameters.json b/modules/Microsoft.EventHub/namespaces/.test/min.parameters.json new file mode 100644 index 0000000..d90c44f --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/.test/min.parameters.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} +} diff --git a/modules/Microsoft.EventHub/namespaces/.test/parameters.json b/modules/Microsoft.EventHub/namespaces/.test/parameters.json new file mode 100644 index 0000000..e997fb3 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/.test/parameters.json @@ -0,0 +1,148 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-evhns-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "authorizationRules": { + "value": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "SendListenAccess", + "rights": [ + "Listen", + "Send" + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "eventHubs": { + "value": [ + { + "name": "<>-az-evh-x-001" + }, + { + "name": "<>-az-evh-x-002", + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "SendListenAccess", + "rights": [ + "Listen", + "Send" + ] + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "messageRetentionInDays": 1, + "partitionCount": 2, + "status": "Active", + "captureDescriptionEnabled": true, + "captureDescriptionEncoding": "Avro", + "captureDescriptionIntervalInSeconds": 300, + "captureDescriptionSizeLimitInBytes": 314572800, + "captureDescriptionDestinationName": "EventHubArchive.AzureBlockBlob", + "captureDescriptionDestinationStorageAccountResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "captureDescriptionDestinationBlobContainer": "eventhub", + "captureDescriptionDestinationArchiveNameFormat": "{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}", + "captureDescriptionSkipEmptyArchives": true, + "consumerGroups": [ + { + "name": "custom", + "userMetadata": "customMetadata" + } + ] + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "namespace", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + } + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "networkRuleSets": { + "value": { + "defaultAction": "Deny", + "ipRules": [ + { + "action": "Allow", + "ipMask": "10.10.10.10" + } + ], + "virtualNetworkRules": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "ignoreMissingVnetServiceEndpoint": true + } + ], + "trustedServiceAccessEnabled": false + } + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} diff --git a/modules/Microsoft.EventHub/namespaces/.test/pe.parameters.json b/modules/Microsoft.EventHub/namespaces/.test/pe.parameters.json new file mode 100644 index 0000000..27acd28 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/.test/pe.parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-evhns-pe-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "namespace", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.EventHub/namespaces/authorizationRules/deploy.bicep b/modules/Microsoft.EventHub/namespaces/authorizationRules/deploy.bicep new file mode 100644 index 0000000..464fd39 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/authorizationRules/deploy.bicep @@ -0,0 +1,49 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Required. The name of the authorization rule.') +param name string + +@description('Optional. The rights associated with the rule.') +@allowed([ + 'Listen' + 'Manage' + 'Send' +]) +param rights array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource authorizationRule 'Microsoft.EventHub/namespaces/AuthorizationRules@2021-11-01' = { + name: name + parent: namespace + properties: { + rights: rights + } +} + +@description('The name of the authorization rule.') +output name string = authorizationRule.name + +@description('The resource ID of the authorization rule.') +output resourceId string = authorizationRule.id + +@description('The name of the resource group the authorization rule was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.EventHub/namespaces/authorizationRules/readme.md b/modules/Microsoft.EventHub/namespaces/authorizationRules/readme.md new file mode 100644 index 0000000..677fc32 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/authorizationRules/readme.md @@ -0,0 +1,47 @@ +# EventHub Namespace Authorization Rule `[Microsoft.EventHub/namespaces/authorizationRules]` + +This module deploys an EventHub Namespace Authorization Rule + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.EventHub/namespaces/authorizationRules` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `rights` | array | `[]` | `[Listen, Manage, Send]` | The rights associated with the rule. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | +| `resourceGroupName` | string | The name of the resource group the authorization rule was created in. | +| `resourceId` | string | The resource ID of the authorization rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/authorizationRules/version.json b/modules/Microsoft.EventHub/namespaces/authorizationRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/authorizationRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventHub/namespaces/deploy.bicep b/modules/Microsoft.EventHub/namespaces/deploy.bicep new file mode 100644 index 0000000..5f7adaa --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/deploy.bicep @@ -0,0 +1,326 @@ +@description('Optional. The name of the event hub namespace. If no name is provided, then unique name will be created.') +@maxLength(50) +param name string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. event hub plan SKU name.') +@allowed([ + 'Basic' + 'Standard' +]) +param skuName string = 'Standard' + +@description('Optional. Event Hub plan scale-out capacity of the resource.') +@minValue(1) +@maxValue(20) +param skuCapacity int = 1 + +@description('Optional. Switch to make the Event Hub Namespace zone redundant.') +param zoneRedundant bool = false + +@description('Optional. Switch to enable the Auto Inflate feature of Event Hub.') +param isAutoInflateEnabled bool = false + +@description('Optional. Upper limit of throughput units when AutoInflate is enabled, value should be within 0 to 20 throughput units.') +@minValue(0) +@maxValue(20) +param maximumThroughputUnits int = 1 + +@description('Optional. Authorization Rules for the Event Hub namespace.') +param authorizationRules array = [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } +] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Configure networking options. This object contains IPs/Subnets to allow or restrict access to private endpoints only. For security reasons, it is recommended to configure this object on the Namespace.') +param networkRuleSets object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The event hubs to deploy into this namespace.') +param eventHubs array = [] + +@description('Optional. The disaster recovery config for this namespace.') +param disasterRecoveryConfig object = {} + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ArchiveLogs' + 'OperationalLogs' + 'AutoScaleLogs' + 'KafkaCoordinatorLogs' + 'KafkaUserErrorLogs' + 'EventHubVNetConnectionEvent' + 'CustomerManagedKeyUserLogs' + 'RuntimeAuditLogs' + 'ApplicationMetricsLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ArchiveLogs' + 'OperationalLogs' + 'AutoScaleLogs' + 'KafkaCoordinatorLogs' + 'KafkaUserErrorLogs' + 'EventHubVNetConnectionEvent' + 'CustomerManagedKeyUserLogs' + 'RuntimeAuditLogs' + 'ApplicationMetricsLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +var uniqueEventHubNamespace = 'evhns-${uniqueString(resourceGroup().id)}' +var name_var = empty(name) ? uniqueEventHubNamespace : name +var maximumThroughputUnits_var = !isAutoInflateEnabled ? 0 : maximumThroughputUnits + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource eventHubNamespace 'Microsoft.EventHub/namespaces@2021-11-01' = { + name: name_var + location: location + tags: tags + identity: identity + sku: { + name: skuName + tier: skuName + capacity: skuCapacity + } + properties: { + zoneRedundant: zoneRedundant + isAutoInflateEnabled: isAutoInflateEnabled + maximumThroughputUnits: maximumThroughputUnits_var + } +} + +module eventHubNamespace_authorizationRules 'authorizationRules/deploy.bicep' = [for (authorizationRule, index) in authorizationRules: { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-AuthRule-${index}' + params: { + namespaceName: eventHubNamespace.name + name: authorizationRule.name + rights: contains(authorizationRule, 'rights') ? authorizationRule.rights : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module eventHubNamespace_disasterRecoveryConfig 'disasterRecoveryConfigs/deploy.bicep' = if (!empty(disasterRecoveryConfig)) { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-DisRecConfig' + params: { + namespaceName: eventHubNamespace.name + name: disasterRecoveryConfig.name + partnerNamespaceId: contains(disasterRecoveryConfig, 'partnerNamespaceId') ? disasterRecoveryConfig.partnerNamespaceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module eventHubNamespace_eventHubs 'eventhubs/deploy.bicep' = [for (eventHub, index) in eventHubs: { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-EventHub-${index}' + params: { + namespaceName: eventHubNamespace.name + name: eventHub.name + authorizationRules: contains(eventHub, 'authorizationRules') ? eventHub.authorizationRules : [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + ] + captureDescriptionDestinationArchiveNameFormat: contains(eventHub, 'captureDescriptionDestinationArchiveNameFormat') ? eventHub.captureDescriptionDestinationArchiveNameFormat : '{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}' + captureDescriptionDestinationBlobContainer: contains(eventHub, 'captureDescriptionDestinationBlobContainer') ? eventHub.captureDescriptionDestinationBlobContainer : '' + captureDescriptionDestinationName: contains(eventHub, 'captureDescriptionDestinationName') ? eventHub.captureDescriptionDestinationName : 'EventHubArchive.AzureBlockBlob' + captureDescriptionDestinationStorageAccountResourceId: contains(eventHub, 'captureDescriptionDestinationStorageAccountResourceId') ? eventHub.captureDescriptionDestinationStorageAccountResourceId : '' + captureDescriptionEnabled: contains(eventHub, 'captureDescriptionEnabled') ? eventHub.captureDescriptionEnabled : false + captureDescriptionEncoding: contains(eventHub, 'captureDescriptionEncoding') ? eventHub.captureDescriptionEncoding : 'Avro' + captureDescriptionIntervalInSeconds: contains(eventHub, 'captureDescriptionIntervalInSeconds') ? eventHub.captureDescriptionIntervalInSeconds : 300 + captureDescriptionSizeLimitInBytes: contains(eventHub, 'captureDescriptionSizeLimitInBytes') ? eventHub.captureDescriptionSizeLimitInBytes : 314572800 + captureDescriptionSkipEmptyArchives: contains(eventHub, 'captureDescriptionSkipEmptyArchives') ? eventHub.captureDescriptionSkipEmptyArchives : false + consumerGroups: contains(eventHub, 'consumerGroups') ? eventHub.consumerGroups : [] + lock: contains(eventHub, 'lock') ? eventHub.lock : '' + messageRetentionInDays: contains(eventHub, 'messageRetentionInDays') ? eventHub.messageRetentionInDays : 1 + partitionCount: contains(eventHub, 'partitionCount') ? eventHub.partitionCount : 2 + roleAssignments: contains(eventHub, 'roleAssignments') ? eventHub.roleAssignments : [] + status: contains(eventHub, 'status') ? eventHub.status : 'Active' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module eventHubNamespace_networkRuleSet 'networkRuleSets/deploy.bicep' = if (!empty(networkRuleSets) || !empty(privateEndpoints)) { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-NetworkRuleSet' + params: { + namespaceName: eventHubNamespace.name + publicNetworkAccess: contains(networkRuleSets, 'publicNetworkAccess') ? networkRuleSets.publicNetworkAccess : (!empty(privateEndpoints) && empty(networkRuleSets) ? 'Disabled' : 'Enabled') + defaultAction: contains(networkRuleSets, 'defaultAction') ? networkRuleSets.defaultAction : 'Allow' + trustedServiceAccessEnabled: contains(networkRuleSets, 'trustedServiceAccessEnabled') ? networkRuleSets.trustedServiceAccessEnabled : true + ipRules: contains(networkRuleSets, 'ipRules') ? networkRuleSets.ipRules : [] + virtualNetworkRules: contains(networkRuleSets, 'virtualNetworkRules') ? networkRuleSets.virtualNetworkRules : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module eventHubNamespace_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(eventHubNamespace.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: eventHubNamespace.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module eventHubNamespace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-EvhbNamespace-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: eventHubNamespace.id + } +}] + +resource eventHubNamespace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${eventHubNamespace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: eventHubNamespace +} + +resource eventHubNamespace_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: eventHubNamespace +} + +@description('The name of the eventspace.') +output name string = eventHubNamespace.name + +@description('The resource ID of the eventspace.') +output resourceId string = eventHubNamespace.id + +@description('The resource group where the namespace is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(eventHubNamespace.identity, 'principalId') ? eventHubNamespace.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = eventHubNamespace.location diff --git a/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/deploy.bicep b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/deploy.bicep new file mode 100644 index 0000000..ce67834 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/deploy.bicep @@ -0,0 +1,44 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Required. The name of the disaster recovery config.') +param name string + +@description('Optional. Resource ID of the Primary/Secondary event hub namespace name, which is part of GEO DR pairing.') +param partnerNamespaceId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource disasterRecoveryConfig 'Microsoft.EventHub/namespaces/disasterRecoveryConfigs@2021-11-01' = { + name: name + parent: namespace + properties: { + partnerNamespace: partnerNamespaceId + } +} + +@description('The name of the disaster recovery config.') +output name string = disasterRecoveryConfig.name + +@description('The resource ID of the disaster recovery config.') +output resourceId string = disasterRecoveryConfig.id + +@description('The name of the resource group the disaster recovery config was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/readme.md b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/readme.md new file mode 100644 index 0000000..fad16d0 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/readme.md @@ -0,0 +1,47 @@ +# EventHub Namespace Disaster Recovery Config `[Microsoft.EventHub/namespaces/disasterRecoveryConfigs]` + +This module deploys an EventHub Namespace Disaster Recovery Config + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.EventHub/namespaces/disasterRecoveryConfigs` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/disasterRecoveryConfigs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the disaster recovery config. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `partnerNamespaceId` | string | `''` | Resource ID of the Primary/Secondary event hub namespace name, which is part of GEO DR pairing. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the disaster recovery config. | +| `resourceGroupName` | string | The name of the resource group the disaster recovery config was created in. | +| `resourceId` | string | The resource ID of the disaster recovery config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/version.json b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/disasterRecoveryConfigs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.EventHub/namespaces/eventhubs/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a20257c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Event Hubs Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f526a384-b230-433a-b45c-95f59c4a2dec') + 'Azure Event Hubs Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a638d3c7-ab3a-418d-83e6-5f17a39d4fde') + 'Azure Event Hubs Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2b629674-e913-4c01-ae53-ef4638d8f975') + '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 eventHub 'Microsoft.EventHub/namespaces/eventhubs@2021-11-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(split(resourceId, '/')[0], split(resourceId, '/')[1], principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: eventHub +}] diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/deploy.bicep b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/deploy.bicep new file mode 100644 index 0000000..db796b1 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/deploy.bicep @@ -0,0 +1,56 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Conditional. The name of the parent event hub namespace event hub. Required if the template is used in a standalone deployment.') +param eventHubName string + +@description('Required. The name of the authorization rule.') +param name string + +@description('Optional. The rights associated with the rule.') +@allowed([ + 'Listen' + 'Manage' + 'Send' +]) +param rights array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName + + resource eventhub 'eventHubs@2021-11-01' existing = { + name: eventHubName + } +} + +resource authorizationRule 'Microsoft.EventHub/namespaces/eventhubs/authorizationRules@2021-11-01' = { + name: name + parent: namespace::eventhub + properties: { + rights: rights + } +} + +@description('The name of the authorization rule.') +output name string = authorizationRule.name + +@description('The resource ID of the authorization rule.') +output resourceId string = authorizationRule.id + +@description('The name of the resource group the authorization rule was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/readme.md b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/readme.md new file mode 100644 index 0000000..c9308aa --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/readme.md @@ -0,0 +1,48 @@ +# EventHub Namespace EventHubs Authorization Rule `[Microsoft.EventHub/namespaces/eventhubs/authorizationRules]` + +This module deploys an EventHub Namespace EventHubs Authorization Rule + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.EventHub/namespaces/eventhubs/authorizationRules` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `eventHubName` | string | The name of the parent event hub namespace event hub. Required if the template is used in a standalone deployment. | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `rights` | array | `[]` | `[Listen, Manage, Send]` | The rights associated with the rule. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | +| `resourceGroupName` | string | The name of the resource group the authorization rule was created in. | +| `resourceId` | string | The resource ID of the authorization rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/version.json b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/authorizationRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/deploy.bicep b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/deploy.bicep new file mode 100644 index 0000000..c6f9f50 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/deploy.bicep @@ -0,0 +1,51 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.s.') +param namespaceName string + +@description('Conditional. The name of the parent event hub namespace event hub. Required if the template is used in a standalone deployment.') +param eventHubName string + +@description('Required. The name of the consumer group.') +param name string + +@description('Optional. User Metadata is a placeholder to store user-defined string data with maximum length 1024. e.g. it can be used to store descriptive data, such as list of teams and their contact information also user-defined configuration settings can be stored.') +param userMetadata string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName + + resource eventhub 'eventHubs@2021-11-01' existing = { + name: eventHubName + } +} + +resource consumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2021-11-01' = { + name: name + parent: namespace::eventhub + properties: { + userMetadata: !empty(userMetadata) ? userMetadata : null + } +} + +@description('The name of the consumer group.') +output name string = consumerGroup.name + +@description('The resource ID of the consumer group.') +output resourceId string = consumerGroup.id + +@description('The name of the resource group the consumer group was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/readme.md b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/readme.md new file mode 100644 index 0000000..6509a18 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/readme.md @@ -0,0 +1,48 @@ +# EventHub Namespace EventHubs Consumer Group `[Microsoft.EventHub/namespaces/eventhubs/consumergroups]` + +This module deploys an EventHub Namespace EventHubs Consumer Group + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.EventHub/namespaces/eventhubs/consumergroups` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/consumergroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the consumer group. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `eventHubName` | string | The name of the parent event hub namespace event hub. Required if the template is used in a standalone deployment. | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment.s. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `userMetadata` | string | `''` | User Metadata is a placeholder to store user-defined string data with maximum length 1024. e.g. it can be used to store descriptive data, such as list of teams and their contact information also user-defined configuration settings can be stored. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the consumer group. | +| `resourceGroupName` | string | The name of the resource group the consumer group was created in. | +| `resourceId` | string | The resource ID of the consumer group. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/version.json b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/consumergroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/deploy.bicep b/modules/Microsoft.EventHub/namespaces/eventhubs/deploy.bicep new file mode 100644 index 0000000..ab6eee8 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/deploy.bicep @@ -0,0 +1,203 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Required. The name of the event hub.') +param name string + +@description('Optional. Authorization Rules for the event hub.') +param authorizationRules array = [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } +] + +@description('Optional. Number of days to retain the events for this Event Hub, value should be 1 to 7 days.') +@minValue(1) +@maxValue(7) +param messageRetentionInDays int = 1 + +@description('Optional. Number of partitions created for the Event Hub, allowed values are from 1 to 32 partitions.') +@minValue(1) +@maxValue(32) +param partitionCount int = 2 + +@description('Optional. Enumerates the possible values for the status of the Event Hub.') +@allowed([ + 'Active' + 'Creating' + 'Deleting' + 'Disabled' + 'ReceiveDisabled' + 'Renaming' + 'Restoring' + 'SendDisabled' + 'Unknown' +]) +param status string = 'Active' + +@description('Optional. The consumer groups to create in this event hub instance.') +param consumerGroups array = [ + { + name: '$Default' + } +] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Name for capture destination.') +param captureDescriptionDestinationName string = 'EventHubArchive.AzureBlockBlob' + +@description('Optional. Blob naming convention for archive, e.g. {Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}. Here all the parameters (Namespace,EventHub .. etc) are mandatory irrespective of order.') +param captureDescriptionDestinationArchiveNameFormat string = '{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}' + +@description('Optional. Blob container Name.') +param captureDescriptionDestinationBlobContainer string = '' + +@description('Optional. Resource ID of the storage account to be used to create the blobs.') +param captureDescriptionDestinationStorageAccountResourceId string = '' + +@description('Optional. A value that indicates whether capture description is enabled.') +param captureDescriptionEnabled bool = false + +@description('Optional. Enumerates the possible values for the encoding format of capture description. Note: "AvroDeflate" will be deprecated in New API Version.') +@allowed([ + 'Avro' + 'AvroDeflate' +]) +param captureDescriptionEncoding string = 'Avro' + +@description('Optional. The time window allows you to set the frequency with which the capture to Azure Blobs will happen.') +@minValue(60) +@maxValue(900) +param captureDescriptionIntervalInSeconds int = 300 + +@description('Optional. The size window defines the amount of data built up in your Event Hub before an capture operation.') +@minValue(10485760) +@maxValue(524288000) +param captureDescriptionSizeLimitInBytes int = 314572800 + +@description('Optional. A value that indicates whether to Skip Empty Archives.') +param captureDescriptionSkipEmptyArchives bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +var eventHubPropertiesSimple = { + messageRetentionInDays: messageRetentionInDays + partitionCount: partitionCount + status: status +} +var eventHubPropertiesWithCapture = { + messageRetentionInDays: messageRetentionInDays + partitionCount: partitionCount + status: status + captureDescription: { + destination: { + name: captureDescriptionDestinationName + properties: { + archiveNameFormat: captureDescriptionDestinationArchiveNameFormat + blobContainer: captureDescriptionDestinationBlobContainer + storageAccountResourceId: captureDescriptionDestinationStorageAccountResourceId + } + } + enabled: captureDescriptionEnabled + encoding: captureDescriptionEncoding + intervalInSeconds: captureDescriptionIntervalInSeconds + sizeLimitInBytes: captureDescriptionSizeLimitInBytes + skipEmptyArchives: captureDescriptionSkipEmptyArchives + } +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2021-11-01' = { + name: name + parent: namespace + properties: captureDescriptionEnabled ? eventHubPropertiesWithCapture : eventHubPropertiesSimple +} + +resource eventHub_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${eventHub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: eventHub +} + +module eventHub_consumergroups 'consumergroups/deploy.bicep' = [for (consumerGroup, index) in consumerGroups: { + name: '${deployment().name}-ConsumerGroup-${index}' + params: { + namespaceName: namespaceName + eventHubName: eventHub.name + name: consumerGroup.name + userMetadata: contains(consumerGroup, 'userMetadata') ? consumerGroup.userMetadata : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module eventHub_authorizationRules 'authorizationRules/deploy.bicep' = [for (authorizationRule, index) in authorizationRules: { + name: '${deployment().name}-AuthRule-${index}' + params: { + namespaceName: namespaceName + eventHubName: eventHub.name + name: authorizationRule.name + rights: contains(authorizationRule, 'rights') ? authorizationRule.rights : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module eventHub_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: eventHub.id + } +}] + +@description('The name of the event hub.') +output name string = eventHub.name + +@description('The resource ID of the event hub.') +output eventHubId string = eventHub.id + +@description('The resource group the event hub was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The authentication rule resource ID of the event hub.') +output resourceId string = az.resourceId('Microsoft.EventHub/namespaces/authorizationRules', namespaceName, 'RootManageSharedAccessKey') diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/readme.md b/modules/Microsoft.EventHub/namespaces/eventhubs/readme.md new file mode 100644 index 0000000..3989e18 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/readme.md @@ -0,0 +1,126 @@ +# EventHub `[Microsoft.EventHub/namespaces/eventhubs]` + +This module deploys an Event Hub. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.EventHub/namespaces/eventhubs` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs) | +| `Microsoft.EventHub/namespaces/eventhubs/authorizationRules` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/authorizationRules) | +| `Microsoft.EventHub/namespaces/eventhubs/consumergroups` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/consumergroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the event hub. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authorizationRules` | _[authorizationRules](authorizationRules/readme.md)_ array | `[System.Collections.Hashtable]` | | Authorization Rules for the event hub. | +| `captureDescriptionDestinationArchiveNameFormat` | string | `'{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}'` | | Blob naming convention for archive, e.g. {Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}. Here all the parameters (Namespace,EventHub .. etc) are mandatory irrespective of order. | +| `captureDescriptionDestinationBlobContainer` | string | `''` | | Blob container Name. | +| `captureDescriptionDestinationName` | string | `'EventHubArchive.AzureBlockBlob'` | | Name for capture destination. | +| `captureDescriptionDestinationStorageAccountResourceId` | string | `''` | | Resource ID of the storage account to be used to create the blobs. | +| `captureDescriptionEnabled` | bool | `False` | | A value that indicates whether capture description is enabled. | +| `captureDescriptionEncoding` | string | `'Avro'` | `[Avro, AvroDeflate]` | Enumerates the possible values for the encoding format of capture description. Note: "AvroDeflate" will be deprecated in New API Version. | +| `captureDescriptionIntervalInSeconds` | int | `300` | | The time window allows you to set the frequency with which the capture to Azure Blobs will happen. | +| `captureDescriptionSizeLimitInBytes` | int | `314572800` | | The size window defines the amount of data built up in your Event Hub before an capture operation. | +| `captureDescriptionSkipEmptyArchives` | bool | `False` | | A value that indicates whether to Skip Empty Archives. | +| `consumerGroups` | _[consumerGroups](consumerGroups/readme.md)_ array | `[System.Collections.Hashtable]` | | The consumer groups to create in this event hub instance. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `messageRetentionInDays` | int | `1` | | Number of days to retain the events for this Event Hub, value should be 1 to 7 days. | +| `partitionCount` | int | `2` | | Number of partitions created for the Event Hub, allowed values are from 1 to 32 partitions. | +| `roleAssignments` | array | `[]` | | 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'. | +| `status` | string | `'Active'` | `[Active, Creating, Deleting, Disabled, ReceiveDisabled, Renaming, Restoring, SendDisabled, Unknown]` | Enumerates the possible values for the status of the Event Hub. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `eventHubId` | string | The resource ID of the event hub. | +| `name` | string | The name of the event hub. | +| `resourceGroupName` | string | The resource group the event hub was deployed into. | +| `resourceId` | string | The authentication rule resource ID of the event hub. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/eventhubs/version.json b/modules/Microsoft.EventHub/namespaces/eventhubs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/eventhubs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.EventHub/namespaces/networkRuleSets/deploy.bicep b/modules/Microsoft.EventHub/namespaces/networkRuleSets/deploy.bicep new file mode 100644 index 0000000..e8907f3 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/networkRuleSets/deploy.bicep @@ -0,0 +1,76 @@ +@description('Conditional. The name of the parent event hub namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. This determines if traffic is allowed over public network. Default is "Enabled". If set to "Disabled", traffic to this namespace will be restricted over Private Endpoints only and network rules will not be applied.') +param publicNetworkAccess string = 'Enabled' + +@allowed([ + 'Allow' + 'Deny' +]) +@description('Optional. Default Action for Network Rule Set. Default is "Allow". It will not be set if publicNetworkAccess is "Disabled". Otherwise, it will be set to "Deny" if ipRules or virtualNetworkRules are being used.') +param defaultAction string = 'Allow' + +@allowed([ + true + false +]) +@description('Optional. Value that indicates whether Trusted Service Access is enabled or not. Default is "true". It will not be set if publicNetworkAccess is "Disabled".') +param trustedServiceAccessEnabled bool = true + +@description('Optional. List virtual network rules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny".') +param virtualNetworkRules array = [] + +@description('Optional. List of IpRules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny".') +param ipRules array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var networkRules = [for (virtualNetworkRule, index) in virtualNetworkRules: { + ignoreMissingVnetServiceEndpoint: contains(virtualNetworkRule, 'ignoreMissingVnetServiceEndpoint') ? virtualNetworkRule.ignoreMissingVnetServiceEndpoint : null + subnet: contains(virtualNetworkRule, 'subnetResourceId') ? { + id: virtualNetworkRule.subnetResourceId + } : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.EventHub/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource networkRuleSet 'Microsoft.EventHub/namespaces/networkRuleSets@2021-11-01' = { + name: 'default' + parent: namespace + properties: { + publicNetworkAccess: publicNetworkAccess + defaultAction: publicNetworkAccess == 'Disabled' ? null : (!empty(ipRules) || !empty(virtualNetworkRules) ? 'Deny' : defaultAction) + trustedServiceAccessEnabled: publicNetworkAccess == 'Disabled' ? null : trustedServiceAccessEnabled + ipRules: publicNetworkAccess == 'Disabled' ? null : ipRules + virtualNetworkRules: publicNetworkAccess == 'Disabled' ? null : networkRules + } +} + +@description('The name of the network rule set.') +output name string = networkRuleSet.name + +@description('The resource ID of the network rule set.') +output resourceId string = networkRuleSet.id + +@description('The name of the resource group the network rule set was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.EventHub/namespaces/networkRuleSets/readme.md b/modules/Microsoft.EventHub/namespaces/networkRuleSets/readme.md new file mode 100644 index 0000000..6e76beb --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/networkRuleSets/readme.md @@ -0,0 +1,88 @@ +# EventHub Namespaces NetworkRuleSets `[Microsoft.EventHub/namespaces/networkRuleSets]` + +This module deploys EventHub Namespaces NetworkRuleSets. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.EventHub/namespaces/networkRuleSets` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/networkRuleSets) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent event hub namespace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `defaultAction` | string | `'Allow'` | `[Allow, Deny]` | Default Action for Network Rule Set. Default is "Allow". It will not be set if publicNetworkAccess is "Disabled". Otherwise, it will be set to "Deny" if ipRules or virtualNetworkRules are being used. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ipRules` | array | `[]` | | List of IpRules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny". | +| `publicNetworkAccess` | string | `'Enabled'` | `[Disabled, Enabled]` | This determines if traffic is allowed over public network. Default is "Enabled". If set to "Disabled", traffic to this namespace will be restricted over Private Endpoints only and network rules will not be applied. | +| `trustedServiceAccessEnabled` | bool | `True` | `[False, True]` | Value that indicates whether Trusted Service Access is enabled or not. Default is "true". It will not be set if publicNetworkAccess is "Disabled". | +| `virtualNetworkRules` | array | `[]` | | List virtual network rules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny". | + + +### Parameter Usage: `` + +Contains an array of subnets that this Event Hub Namespace is exposed to via Service Endpoints. You can enable the `ignoreMissingVnetServiceEndpoint` if you wish to add this virtual network to Event Hub Namespace but do not have an existing service endpoint. + +```json +"virtualNetworkRules": { + "value": [ + { + "ignoreMissingVnetServiceEndpoint": true, + "subnet": { + "id": "/subscriptions/<>/resourcegroups/<>/providers/Microsoft.Network/virtualNetworks/<>/subnets/<>" + } + }, + { + "ignoreMissingVnetServiceEndpoint": false, + "subnet": { + "id": "/subscriptions/<>/resourcegroups/<>/providers/Microsoft.Network/virtualNetworks/<>/subnets/<>" + } + } + ] +} +``` + +### Parameter Usage: `` + +Contains an array of objects for the public IP ranges you want to allow via the Event Hub Namespace firewall. Supports IPv4 address or CIDR. + +```json +"ipRules": { + "value": [ + { + "action": "Allow", + "ipMask": "a.b.c.d/e" + }, + { + "action": "Allow", + "ipMask": "x.x.x.x/x" + } + ] +} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the network rule set. | +| `resourceGroupName` | string | The name of the resource group the network rule set was created in. | +| `resourceId` | string | The resource ID of the network rule set. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.EventHub/namespaces/networkRuleSets/version.json b/modules/Microsoft.EventHub/namespaces/networkRuleSets/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/networkRuleSets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.EventHub/namespaces/readme.md b/modules/Microsoft.EventHub/namespaces/readme.md new file mode 100644 index 0000000..0db1a66 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/readme.md @@ -0,0 +1,683 @@ +# Event Hub Namespaces `[Microsoft.EventHub/namespaces]` + +This module deploys an event hub namespace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.EventHub/namespaces` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces) | +| `Microsoft.EventHub/namespaces/authorizationRules` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/authorizationRules) | +| `Microsoft.EventHub/namespaces/disasterRecoveryConfigs` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/disasterRecoveryConfigs) | +| `Microsoft.EventHub/namespaces/eventhubs` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs) | +| `Microsoft.EventHub/namespaces/eventhubs/authorizationRules` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/authorizationRules) | +| `Microsoft.EventHub/namespaces/eventhubs/consumergroups` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/eventhubs/consumergroups) | +| `Microsoft.EventHub/namespaces/networkRuleSets` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2021-11-01/namespaces/networkRuleSets) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authorizationRules` | _[authorizationRules](authorizationRules/readme.md)_ array | `[System.Collections.Hashtable]` | | Authorization Rules for the Event Hub namespace. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ApplicationMetricsLogs, ArchiveLogs, AutoScaleLogs, CustomerManagedKeyUserLogs, EventHubVNetConnectionEvent, KafkaCoordinatorLogs, KafkaUserErrorLogs, OperationalLogs, RuntimeAuditLogs]` | `[ApplicationMetricsLogs, ArchiveLogs, AutoScaleLogs, CustomerManagedKeyUserLogs, EventHubVNetConnectionEvent, KafkaCoordinatorLogs, KafkaUserErrorLogs, OperationalLogs, RuntimeAuditLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disasterRecoveryConfig` | object | `{object}` | | The disaster recovery config for this namespace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `eventHubs` | _[eventHubs](eventHubs/readme.md)_ array | `[]` | | The event hubs to deploy into this namespace. | +| `isAutoInflateEnabled` | bool | `False` | | Switch to enable the Auto Inflate feature of Event Hub. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maximumThroughputUnits` | int | `1` | | Upper limit of throughput units when AutoInflate is enabled, value should be within 0 to 20 throughput units. | +| `name` | string | `''` | | The name of the event hub namespace. If no name is provided, then unique name will be created. | +| `networkRuleSets` | _[networkRuleSets](networkRuleSets/readme.md)_ object | `{object}` | | Configure networking options. This object contains IPs/Subnets to allow or restrict access to private endpoints only. For security reasons, it is recommended to configure this object on the Namespace. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `roleAssignments` | array | `[]` | | 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'. | +| `skuCapacity` | int | `1` | | Event Hub plan scale-out capacity of the resource. | +| `skuName` | string | `'Standard'` | `[Basic, Standard]` | event hub plan SKU name. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `zoneRedundant` | bool | `False` | | Switch to make the Event Hub Namespace zone redundant. | + + +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the eventspace. | +| `resourceGroupName` | string | The resource group where the namespace is deployed. | +| `resourceId` | string | The resource ID of the eventspace. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.EventHub/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.EventHub/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'SendListenAccess' + rights: [ + 'Listen' + 'Send' + ] + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + eventHubs: [ + { + name: '<>-az-evh-x-001' + } + { + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'SendListenAccess' + rights: [ + 'Listen' + 'Send' + ] + } + ] + captureDescriptionDestinationArchiveNameFormat: '{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}' + captureDescriptionDestinationBlobContainer: 'eventhub' + captureDescriptionDestinationName: 'EventHubArchive.AzureBlockBlob' + captureDescriptionDestinationStorageAccountResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + captureDescriptionEnabled: true + captureDescriptionEncoding: 'Avro' + captureDescriptionIntervalInSeconds: 300 + captureDescriptionSizeLimitInBytes: 314572800 + captureDescriptionSkipEmptyArchives: true + consumerGroups: [ + { + name: 'custom' + userMetadata: 'customMetadata' + } + ] + messageRetentionInDays: 1 + name: '<>-az-evh-x-002' + partitionCount: 2 + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + status: 'Active' + } + ] + lock: 'CanNotDelete' + name: '<>-az-evhns-x-001' + networkRuleSets: { + defaultAction: 'Deny' + ipRules: [ + { + action: 'Allow' + ipMask: '10.10.10.10' + } + ] + trustedServiceAccessEnabled: false + virtualNetworkRules: [ + { + ignoreMissingVnetServiceEndpoint: true + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net' + ] + } + service: 'namespace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authorizationRules": { + "value": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "SendListenAccess", + "rights": [ + "Listen", + "Send" + ] + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "eventHubs": { + "value": [ + { + "name": "<>-az-evh-x-001" + }, + { + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "SendListenAccess", + "rights": [ + "Listen", + "Send" + ] + } + ], + "captureDescriptionDestinationArchiveNameFormat": "{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}", + "captureDescriptionDestinationBlobContainer": "eventhub", + "captureDescriptionDestinationName": "EventHubArchive.AzureBlockBlob", + "captureDescriptionDestinationStorageAccountResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "captureDescriptionEnabled": true, + "captureDescriptionEncoding": "Avro", + "captureDescriptionIntervalInSeconds": 300, + "captureDescriptionSizeLimitInBytes": 314572800, + "captureDescriptionSkipEmptyArchives": true, + "consumerGroups": [ + { + "name": "custom", + "userMetadata": "customMetadata" + } + ], + "messageRetentionInDays": 1, + "name": "<>-az-evh-x-002", + "partitionCount": 2, + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "status": "Active" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "name": { + "value": "<>-az-evhns-x-001" + }, + "networkRuleSets": { + "value": { + "defaultAction": "Deny", + "ipRules": [ + { + "action": "Allow", + "ipMask": "10.10.10.10" + } + ], + "trustedServiceAccessEnabled": false, + "virtualNetworkRules": [ + { + "ignoreMissingVnetServiceEndpoint": true, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + }, + "service": "namespace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.EventHub/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + name: '<>-az-evhns-pe-001' + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net' + ] + } + service: 'namespace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-evhns-pe-001" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + }, + "service": "namespace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.EventHub/namespaces/version.json b/modules/Microsoft.EventHub/namespaces/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.EventHub/namespaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.HealthBot/healthBots/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.HealthBot/healthBots/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ba386a4 --- /dev/null +++ b/modules/Microsoft.HealthBot/healthBots/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 healthBot 'Microsoft.HealthBot/healthBots@2021-06-10' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(healthBot.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: healthBot +}] diff --git a/modules/Microsoft.HealthBot/healthBots/.test/parameters.json b/modules/Microsoft.HealthBot/healthBots/.test/parameters.json new file mode 100644 index 0000000..fef2b74 --- /dev/null +++ b/modules/Microsoft.HealthBot/healthBots/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ahb-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.HealthBot/healthBots/deploy.bicep b/modules/Microsoft.HealthBot/healthBots/deploy.bicep new file mode 100644 index 0000000..10fe31f --- /dev/null +++ b/modules/Microsoft.HealthBot/healthBots/deploy.bicep @@ -0,0 +1,81 @@ +@description('Required. Name of the resource.') +param name string + +@description('Optional. The resource model definition representing SKU.') +param sku string = 'F0' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource azureHealthBot 'Microsoft.HealthBot/healthBots@2020-12-08' = { + name: name + location: location + tags: tags + sku: { + name: sku + } + properties: {} +} + +resource azureHealthBot_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${azureHealthBot.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: azureHealthBot +} + +module healthBot_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-HealthBot-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: azureHealthBot.id + } +}] + +@description('The resource group the health bot was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the health bot.') +output name string = azureHealthBot.name + +@description('The resource ID of the health bot.') +output resourceId string = azureHealthBot.id + +@description('The location the resource was deployed into.') +output location string = azureHealthBot.location diff --git a/modules/Microsoft.HealthBot/healthBots/readme.md b/modules/Microsoft.HealthBot/healthBots/readme.md new file mode 100644 index 0000000..b632120 --- /dev/null +++ b/modules/Microsoft.HealthBot/healthBots/readme.md @@ -0,0 +1,220 @@ +# Azure Health Bots `[Microsoft.HealthBot/healthBots]` + +This module deploys an Azure Health Bot. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.HealthBot/healthBots` | [2020-12-08](https://docs.microsoft.com/en-us/azure/templates/Microsoft.HealthBot/2020-12-08/healthBots) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'F0'` | | The resource model definition representing SKU. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the health bot. | +| `resourceGroupName` | string | The resource group the health bot was deployed into. | +| `resourceId` | string | The resource ID of the health bot. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module healthBots './Microsoft.HealthBot/healthBots/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-HealthBots' + params: { + // Required parameters + name: '<>-az-ahb-x-001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-ahb-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.HealthBot/healthBots/version.json b/modules/Microsoft.HealthBot/healthBots/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.HealthBot/healthBots/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..03d7272 --- /dev/null +++ b/modules/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + '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 actionGroup 'microsoft.insights/actionGroups@2019-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(actionGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: actionGroup +}] diff --git a/modules/Microsoft.Insights/actionGroups/.test/parameters.json b/modules/Microsoft.Insights/actionGroups/.test/parameters.json new file mode 100644 index 0000000..32c9e76 --- /dev/null +++ b/modules/Microsoft.Insights/actionGroups/.test/parameters.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ag-x-001" + }, + "groupShortName": { + "value": "azagweux001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "emailReceivers": { + "value": [ + { + "name": "TestUser_-EmailAction-", + "emailAddress": "test.user@testcompany.com", + "useCommonAlertSchema": true + }, + { + "name": "TestUser2", + "emailAddress": "test.user2@testcompany.com", + "useCommonAlertSchema": true + } + ] + }, + "smsReceivers": { + "value": [ + { + "name": "TestUser_-SMSAction-", + "countryCode": "1", + "phoneNumber": "2345678901" + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/actionGroups/deploy.bicep b/modules/Microsoft.Insights/actionGroups/deploy.bicep new file mode 100644 index 0000000..204c138 --- /dev/null +++ b/modules/Microsoft.Insights/actionGroups/deploy.bicep @@ -0,0 +1,107 @@ +@description('Required. The name of the action group.') +param name string + +@description('Required. The short name of the action group.') +param groupShortName string + +@description('Optional. Indicates whether this action group is enabled. If an action group is not enabled, then none of its receivers will receive communications.') +param enabled bool = true + +@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. The list of email receivers that are part of this action group.') +param emailReceivers array = [] + +@description('Optional. The list of SMS receivers that are part of this action group.') +param smsReceivers array = [] + +@description('Optional. The list of webhook receivers that are part of this action group.') +param webhookReceivers array = [] + +@description('Optional. The list of ITSM receivers that are part of this action group.') +param itsmReceivers array = [] + +@description('Optional. The list of AzureAppPush receivers that are part of this action group.') +param azureAppPushReceivers array = [] + +@description('Optional. The list of AutomationRunbook receivers that are part of this action group.') +param automationRunbookReceivers array = [] + +@description('Optional. The list of voice receivers that are part of this action group.') +param voiceReceivers array = [] + +@description('Optional. The list of logic app receivers that are part of this action group.') +param logicAppReceivers array = [] + +@description('Optional. The list of function receivers that are part of this action group.') +param azureFunctionReceivers array = [] + +@description('Optional. The list of ARM role receivers that are part of this action group. Roles are Azure RBAC roles and only built-in roles are supported.') +param armRoleReceivers array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Location for all resources.') +param location string = 'global' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource actionGroup 'microsoft.insights/actionGroups@2019-06-01' = { + name: name + location: location + tags: tags + properties: { + groupShortName: groupShortName + enabled: enabled + emailReceivers: (empty(emailReceivers) ? null : emailReceivers) + smsReceivers: (empty(smsReceivers) ? null : smsReceivers) + webhookReceivers: (empty(webhookReceivers) ? null : webhookReceivers) + itsmReceivers: (empty(itsmReceivers) ? null : itsmReceivers) + azureAppPushReceivers: (empty(azureAppPushReceivers) ? null : azureAppPushReceivers) + automationRunbookReceivers: (empty(automationRunbookReceivers) ? null : automationRunbookReceivers) + voiceReceivers: (empty(voiceReceivers) ? null : voiceReceivers) + logicAppReceivers: (empty(logicAppReceivers) ? null : logicAppReceivers) + azureFunctionReceivers: (empty(azureFunctionReceivers) ? null : azureFunctionReceivers) + armRoleReceivers: (empty(armRoleReceivers) ? null : armRoleReceivers) + } +} + +module actionGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ActionGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: actionGroup.id + } +}] + +@description('The resource group the action group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the action group .') +output name string = actionGroup.name + +@description('The resource ID of the action group .') +output resourceId string = actionGroup.id + +@description('The location the resource was deployed into.') +output location string = actionGroup.location diff --git a/modules/Microsoft.Insights/actionGroups/readme.md b/modules/Microsoft.Insights/actionGroups/readme.md new file mode 100644 index 0000000..21b0ddd --- /dev/null +++ b/modules/Microsoft.Insights/actionGroups/readme.md @@ -0,0 +1,348 @@ +# Action Groups `[Microsoft.Insights/actionGroups]` + +This module deploys an Action Group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `microsoft.insights/actionGroups` | [2019-06-01](https://docs.microsoft.com/en-us/azure/templates/microsoft.insights/2019-06-01/actionGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `groupShortName` | string | The short name of the action group. | +| `name` | string | The name of the action group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `armRoleReceivers` | array | `[]` | The list of ARM role receivers that are part of this action group. Roles are Azure RBAC roles and only built-in roles are supported. | +| `automationRunbookReceivers` | array | `[]` | The list of AutomationRunbook receivers that are part of this action group. | +| `azureAppPushReceivers` | array | `[]` | The list of AzureAppPush receivers that are part of this action group. | +| `azureFunctionReceivers` | array | `[]` | The list of function receivers that are part of this action group. | +| `emailReceivers` | array | `[]` | The list of email receivers that are part of this action group. | +| `enabled` | bool | `True` | Indicates whether this action group is enabled. If an action group is not enabled, then none of its receivers will receive communications. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `itsmReceivers` | array | `[]` | The list of ITSM receivers that are part of this action group. | +| `location` | string | `'global'` | Location for all resources. | +| `logicAppReceivers` | array | `[]` | The list of logic app receivers that are part of this action group. | +| `roleAssignments` | array | `[]` | 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'. | +| `smsReceivers` | array | `[]` | The list of SMS receivers that are part of this action group. | +| `tags` | object | `{object}` | Tags of the resource. | +| `voiceReceivers` | array | `[]` | The list of voice receivers that are part of this action group. | +| `webhookReceivers` | array | `[]` | The list of webhook receivers that are part of this action group. | + + +### Parameter Usage: receivers + +See [Documentation](https://docs.microsoft.com/en-us/azure/templates/microsoft.insights/2019-06-01/actiongroups) for description of parameters usage and syntax. + +

+ +Parameter JSON file + +```json +"emailReceivers": { + "value": [ + { + "name": "TestUser_-EmailAction-", + "emailAddress": "test.user@testcompany.com", + "useCommonAlertSchema": true + }, + { + "name": "TestUser2", + "emailAddress": "test.user2@testcompany.com", + "useCommonAlertSchema": true + } + ] +}, +"smsReceivers": { + "value": [ + { + "name": "TestUser_-SMSAction-", + "countryCode": "1", + "phoneNumber": "2345678901" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +emailReceivers: [ + { + name: 'TestUser_-EmailAction-' + emailAddress: 'test.user@testcompany.com' + useCommonAlertSchema: true + } + { + name: 'TestUser2' + emailAddress: 'test.user2@testcompany.com' + useCommonAlertSchema: true + } +] +smsReceivers: [ + { + name: 'TestUser_-SMSAction-' + countryCode: '1' + phoneNumber: '2345678901' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Additional notes on parameters + +- Receiver name must be unique across the ActionGroup +- Email, SMS, Azure App push and Voice can be grouped in the same Action. To do so, the `name` field of the receivers must be in the `RecName_-ActionType-` format where: + - _RecName_ is the name you want to give to the Action + - _ActionType_ is one of the action types that can be grouped together. Possible values are: + - EmailAction + - SMSAction + - AzureAppAction + - VoiceAction +- To understand the impact of the `useCommonAlertSchema` field, see [here](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/alerts-common-schema) + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the action group . | +| `resourceGroupName` | string | The resource group the action group was deployed into. | +| `resourceId` | string | The resource ID of the action group . | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module actionGroups './Microsoft.Insights/actionGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ActionGroups' + params: { + // Required parameters + groupShortName: 'azagweux001' + name: '<>-az-ag-x-001' + // Non-required parameters + emailReceivers: [ + { + emailAddress: 'test.user@testcompany.com' + name: 'TestUser_-EmailAction-' + useCommonAlertSchema: true + } + { + emailAddress: 'test.user2@testcompany.com' + name: 'TestUser2' + useCommonAlertSchema: true + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + smsReceivers: [ + { + countryCode: '1' + name: 'TestUser_-SMSAction-' + phoneNumber: '2345678901' + } + ] + } +} +``` + +
+

+ +

+ +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 + "groupShortName": { + "value": "azagweux001" + }, + "name": { + "value": "<>-az-ag-x-001" + }, + // Non-required parameters + "emailReceivers": { + "value": [ + { + "emailAddress": "test.user@testcompany.com", + "name": "TestUser_-EmailAction-", + "useCommonAlertSchema": true + }, + { + "emailAddress": "test.user2@testcompany.com", + "name": "TestUser2", + "useCommonAlertSchema": true + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "smsReceivers": { + "value": [ + { + "countryCode": "1", + "name": "TestUser_-SMSAction-", + "phoneNumber": "2345678901" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/actionGroups/version.json b/modules/Microsoft.Insights/actionGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/actionGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..367ed2d --- /dev/null +++ b/modules/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + '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 activityLogAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(activityLogAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: activityLogAlert +}] diff --git a/modules/Microsoft.Insights/activityLogAlerts/.test/parameters.json b/modules/Microsoft.Insights/activityLogAlerts/.test/parameters.json new file mode 100644 index 0000000..8d7e3e6 --- /dev/null +++ b/modules/Microsoft.Insights/activityLogAlerts/.test/parameters.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ala-x-001" + }, + "scopes": { + "value": [ + "/subscriptions/<>" + ] + }, + "conditions": { + "value": [ + { + "field": "category", + "equals": "Administrative" + }, + { + "field": "resourceType", + "equals": "microsoft.compute/virtualmachines" + }, + { + "field": "operationName", + "equals": "Microsoft.Compute/virtualMachines/performMaintenance/action" + } + ] + }, + "actions": { + "value": [ + { + "actionGroupId": "/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/activityLogAlerts/deploy.bicep b/modules/Microsoft.Insights/activityLogAlerts/deploy.bicep new file mode 100644 index 0000000..f4a798b --- /dev/null +++ b/modules/Microsoft.Insights/activityLogAlerts/deploy.bicep @@ -0,0 +1,90 @@ +@description('Required. The name of the alert.') +param name string + +@description('Optional. Description of the alert.') +param alertDescription string = '' + +@description('Optional. Location for all resources.') +param location string = 'global' + +@description('Optional. Indicates whether this alert is enabled.') +param enabled bool = true + +@description('Optional. The list of resource IDs that this metric alert is scoped to.') +param scopes array = [ + subscription().id +] + +@description('Optional. The list of actions to take when alert triggers.') +param actions array = [] + +@description('Required. The condition that will cause this alert to activate. Array of objects.') +param conditions array + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var actionGroups = [for action in actions: { + actionGroupId: contains(action, 'actionGroupId') ? action.actionGroupId : action + webhookProperties: contains(action, 'webhookProperties') ? action.webhookProperties : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource activityLogAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' = { + name: name + location: location + tags: tags + properties: { + scopes: scopes + condition: { + allOf: conditions + } + actions: { + actionGroups: actionGroups + } + enabled: enabled + description: alertDescription + } +} + +module activityLogAlert_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ActivityLogAlert-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: activityLogAlert.id + } +}] + +@description('The name of the activity log alert.') +output name string = activityLogAlert.name + +@description('The resource ID of the activity log alert.') +output resourceId string = activityLogAlert.id + +@description('The resource group the activity log alert was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = activityLogAlert.location diff --git a/modules/Microsoft.Insights/activityLogAlerts/readme.md b/modules/Microsoft.Insights/activityLogAlerts/readme.md new file mode 100644 index 0000000..718d660 --- /dev/null +++ b/modules/Microsoft.Insights/activityLogAlerts/readme.md @@ -0,0 +1,514 @@ +# Activity Log Alerts `[Microsoft.Insights/activityLogAlerts]` + +This module deploys an Alert based on Activity Log. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/activityLogAlerts` | [2020-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-10-01/activityLogAlerts) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `conditions` | array | The condition that will cause this alert to activate. Array of objects. | +| `name` | string | The name of the alert. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | The list of actions to take when alert triggers. | +| `alertDescription` | string | `''` | Description of the alert. | +| `enabled` | bool | `True` | Indicates whether this alert is enabled. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `'global'` | Location for all resources. | +| `roleAssignments` | array | `[]` | 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'. | +| `scopes` | array | `[[subscription().id]]` | The list of resource IDs that this metric alert is scoped to. | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: actions + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + { + "actionGroupId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName", + "webhookProperties": {} + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + { + actionGroupId: '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName' + webhookProperties: {} + } +] +``` + +
+

+ +`webhookProperties` is optional. + +If you do only want to provide actionGroupIds, a shorthand use of the parameter is available. + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName' +] +``` + +
+

+ +### Parameter Usage: conditions + +**Conditions can also be combined with logical operators `allOf` and `anyOf`** + + +

+ +Parameter JSON format + +```json +{ + "field": "string", + "equals": "string", + "containsAny": "array" +} +``` + +
+ +
+ +Bicep format + +```bicep +{ + field: 'string' + equals: 'string' + containsAny: 'array' +} +``` + +
+

+ +Each condition can specify only one field between `equals` and `containsAny`. + +| Parameter Name | Type | Possible values | Description | +| :------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | +| `field` | string | `resourceId`,
`category`,
`caller`,
`level`,
`operationName`,
`resourceGroup`,
`resourceProvider`,
`status`,
`subStatus`,
`resourceType`,
or anything beginning with `properties.` | Required. The name of the field that this condition will examine. | +| `equals` | string | | Optional (Alternative to `containsAny`). The value to confront with. | +| `containsAny` | array of strings | | Optional (Alternative to `equals`). Condition will be satisfied if value of the field in the event is within one of the specified here. | + +**Sample** + +
+ +Parameter JSON format + +```json +"conditions": { + "value": [ + { + "field": "category", + "equals": "Administrative" + }, + { + "field": "resourceType", + "equals": "microsoft.compute/virtualmachines" + }, + { + "field": "operationName", + "equals": "Microsoft.Compute/virtualMachines/performMaintenance/action" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +conditions: [ + { + field: 'category' + equals: 'Administrative' + } + { + field: 'resourceType' + equals: 'microsoft.compute/virtualmachines' + } + { + field: 'operationName' + equals: 'Microsoft.Compute/virtualMachines/performMaintenance/action' + } +] +``` + +
+

+ +**Sample 2** + +

+ +Parameter JSON format + +```json +"conditions":{ + "value": [ + { + "field": "category", + "equals": "ServiceHealth" + }, + { + "anyOf": [ + { + "field": "properties.incidentType", + "equals": "Incident" + }, + { + "field": "properties.incidentType", + "equals": "Maintenance" + } + ] + }, + { + "field": "properties.impactedServices[*].ServiceName", + "containsAny": [ + "Action Groups", + "Activity Logs & Alerts" + ] + }, + { + "field": "properties.impactedServices[*].ImpactedRegions[*].RegionName", + "containsAny": [ + "West Europe", + "Global" + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +conditions: [ + { + field: 'category' + equals: 'ServiceHealth' + } + { + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Incident' + } + { + field: 'properties.incidentType' + equals: 'Maintenance' + } + ] + } + { + field: 'properties.impactedServices[*].ServiceName' + containsAny: [ + 'Action Groups' + 'Activity Logs & Alerts' + ] + } + { + field: 'properties.impactedServices[*].ImpactedRegions[*].RegionName' + containsAny: [ + 'West Europe' + 'Global' + ] + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the activity log alert. | +| `resourceGroupName` | string | The resource group the activity log alert was deployed into. | +| `resourceId` | string | The resource ID of the activity log alert. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module activityLogAlerts './Microsoft.Insights/activityLogAlerts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ActivityLogAlerts' + params: { + // Required parameters + conditions: [ + { + equals: 'Administrative' + field: 'category' + } + { + equals: 'microsoft.compute/virtualmachines' + field: 'resourceType' + } + { + equals: 'Microsoft.Compute/virtualMachines/performMaintenance/action' + field: 'operationName' + } + ] + name: '<>-az-ala-x-001' + // Non-required parameters + actions: [ + { + actionGroupId: '/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + scopes: [ + '/subscriptions/<>' + ] + } +} +``` + +
+

+ +

+ +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 + "conditions": { + "value": [ + { + "equals": "Administrative", + "field": "category" + }, + { + "equals": "microsoft.compute/virtualmachines", + "field": "resourceType" + }, + { + "equals": "Microsoft.Compute/virtualMachines/performMaintenance/action", + "field": "operationName" + } + ] + }, + "name": { + "value": "<>-az-ala-x-001" + }, + // Non-required parameters + "actions": { + "value": [ + { + "actionGroupId": "/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopes": { + "value": [ + "/subscriptions/<>" + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/activityLogAlerts/version.json b/modules/Microsoft.Insights/activityLogAlerts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/activityLogAlerts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..261a834 --- /dev/null +++ b/modules/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + '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') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appInsights.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appInsights +}] diff --git a/modules/Microsoft.Insights/components/.test/parameters.json b/modules/Microsoft.Insights/components/.test/parameters.json new file mode 100644 index 0000000..636d9f6 --- /dev/null +++ b/modules/Microsoft.Insights/components/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appi-x-001" + }, + "workspaceResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-appi-001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/components/deploy.bicep b/modules/Microsoft.Insights/components/deploy.bicep new file mode 100644 index 0000000..9eacc50 --- /dev/null +++ b/modules/Microsoft.Insights/components/deploy.bicep @@ -0,0 +1,118 @@ +@description('Required. Name of the Application Insights.') +param name string + +@description('Optional. Application type.') +@allowed([ + 'web' + 'other' +]) +param appInsightsType string = 'web' + +@description('Required. Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property.') +param workspaceResourceId string + +@description('Optional. The network access type for accessing Application Insights ingestion. - Enabled or Disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForIngestion string = 'Enabled' + +@description('Optional. The network access type for accessing Application Insights query. - Enabled or Disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForQuery string = 'Enabled' + +@description('Optional. Retention period in days.') +@allowed([ + 30 + 60 + 90 + 120 + 180 + 270 + 365 + 550 + 730 +]) +param retentionInDays int = 365 + +@description('Optional. Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry.') +@minValue(0) +@maxValue(100) +param samplingPercentage int = 100 + +@description('Optional. The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone.') +param kind string = '' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: kind + properties: { + Application_Type: appInsightsType + WorkspaceResourceId: workspaceResourceId + publicNetworkAccessForIngestion: publicNetworkAccessForIngestion + publicNetworkAccessForQuery: publicNetworkAccessForQuery + RetentionInDays: retentionInDays + SamplingPercentage: samplingPercentage + } +} + +module appInsights_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppInsights-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: appInsights.id + } +}] + +@description('The name of the application insights component.') +output name string = appInsights.name + +@description('The resource ID of the application insights component.') +output resourceId string = appInsights.id + +@description('The resource group the application insights component was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The application ID of the application insights component.') +output applicationId string = appInsights.properties.AppId + +@description('The location the resource was deployed into.') +output location string = appInsights.location + +@description('Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component.') +output instrumentationKey string = appInsights.properties.InstrumentationKey diff --git a/modules/Microsoft.Insights/components/readme.md b/modules/Microsoft.Insights/components/readme.md new file mode 100644 index 0000000..6677bca --- /dev/null +++ b/modules/Microsoft.Insights/components/readme.md @@ -0,0 +1,224 @@ +# Application Insights `[Microsoft.Insights/components]` + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/components` | [2020-02-02](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-02-02/components) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Insights. | +| `workspaceResourceId` | string | Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `appInsightsType` | string | `'web'` | `[other, web]` | Application type. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `kind` | string | `''` | | The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `publicNetworkAccessForIngestion` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Application Insights ingestion. - Enabled or Disabled. | +| `publicNetworkAccessForQuery` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Application Insights query. - Enabled or Disabled. | +| `retentionInDays` | int | `365` | `[30, 60, 90, 120, 180, 270, 365, 550, 730]` | Retention period in days. | +| `roleAssignments` | array | `[]` | | 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'. | +| `samplingPercentage` | int | `100` | | Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `applicationId` | string | The application ID of the application insights component. | +| `instrumentationKey` | string | Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application insights component. | +| `resourceGroupName` | string | The resource group the application insights component was deployed into. | +| `resourceId` | string | The resource ID of the application insights component. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module components './Microsoft.Insights/components/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Components' + params: { + // Required parameters + name: '<>-az-appi-x-001' + workspaceResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-appi-001' + // Non-required parameters + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-appi-x-001" + }, + "workspaceResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-appi-001" + }, + // Non-required parameters + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/components/version.json b/modules/Microsoft.Insights/components/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/components/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/diagnosticSettings/.test/parameters.json b/modules/Microsoft.Insights/diagnosticSettings/.test/parameters.json new file mode 100644 index 0000000..2e1d385 --- /dev/null +++ b/modules/Microsoft.Insights/diagnosticSettings/.test/parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-diag-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Insights/diagnosticSettings/deploy.bicep b/modules/Microsoft.Insights/diagnosticSettings/deploy.bicep new file mode 100644 index 0000000..7e31294 --- /dev/null +++ b/modules/Microsoft.Insights/diagnosticSettings/deploy.bicep @@ -0,0 +1,93 @@ +targetScope = 'subscription' + +@description('Optional. Name of the ActivityLog diagnostic settings.') +@minLength(1) +@maxLength(260) +param name string = '${uniqueString(subscription().id)}-ActivityLog' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'Administrative' + 'Security' + 'ServiceHealth' + 'Alert' + 'Recommendation' + 'Policy' + 'Autoscale' + 'ResourceHealth' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Administrative' + 'Security' + 'ServiceHealth' + 'Alert' + 'Recommendation' + 'Policy' + 'Autoscale' + 'ResourceHealth' +] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: name + properties: { + storageAccountId: (empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId) + workspaceId: (empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId) + eventHubAuthorizationRuleId: (empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId) + eventHubName: (empty(diagnosticEventHubName) ? null : diagnosticEventHubName) + logs: ((empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName)) ? null : diagnosticsLogs) + } +} + +@description('The name of the diagnostic settings.') +output name string = diagnosticSetting.name + +@description('The resource ID of the diagnostic settings.') +output resourceId string = diagnosticSetting.id + +@description('The name of the subscription to deploy into.') +output subscriptionName string = subscription().displayName diff --git a/modules/Microsoft.Insights/diagnosticSettings/readme.md b/modules/Microsoft.Insights/diagnosticSettings/readme.md new file mode 100644 index 0000000..5ec7eb3 --- /dev/null +++ b/modules/Microsoft.Insights/diagnosticSettings/readme.md @@ -0,0 +1,109 @@ +# Activity Logs `[Microsoft.Insights/diagnosticSettings]` + +This module deploys a subscription wide export of the activity log. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Administrative, Alert, Autoscale, Policy, Recommendation, ResourceHealth, Security, ServiceHealth]` | `[Administrative, Alert, Autoscale, Policy, Recommendation, ResourceHealth, Security, ServiceHealth]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `name` | string | `[format('{0}-ActivityLog', uniqueString(subscription().id))]` | | Name of the ActivityLog diagnostic settings. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the diagnostic settings. | +| `resourceId` | string | The resource ID of the diagnostic settings. | +| `subscriptionName` | string | The name of the subscription to deploy into. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module diagnosticSettings './Microsoft.Insights/diagnosticSettings/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DiagnosticSettings' + params: { + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + name: '<>-az-diag-x-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "name": { + "value": "<>-az-diag-x-001" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/diagnosticSettings/version.json b/modules/Microsoft.Insights/diagnosticSettings/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/diagnosticSettings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..5bd2ee2 --- /dev/null +++ b/modules/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + '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 metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(metricAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: metricAlert +}] diff --git a/modules/Microsoft.Insights/metricAlerts/.test/parameters.json b/modules/Microsoft.Insights/metricAlerts/.test/parameters.json new file mode 100644 index 0000000..bbe65cd --- /dev/null +++ b/modules/Microsoft.Insights/metricAlerts/.test/parameters.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ma-x-001" + }, + "windowSize": { + "value": "PT15M" + }, + "actions": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001" + ] + }, + "targetResourceType": { + "value": "microsoft.compute/virtualmachines" + }, + "targetResourceRegion": { + "value": "westeurope" + }, + "criterias": { + "value": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ] + }, + "alertCriteriaType": { + "value": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/metricAlerts/deploy.bicep b/modules/Microsoft.Insights/metricAlerts/deploy.bicep new file mode 100644 index 0000000..547fb36 --- /dev/null +++ b/modules/Microsoft.Insights/metricAlerts/deploy.bicep @@ -0,0 +1,145 @@ +@description('Required. The name of the alert.') +param name string + +@description('Optional. Description of the alert.') +param alertDescription string = '' + +@description('Optional. Location for all resources.') +param location string = 'global' + +@description('Optional. Indicates whether this alert is enabled.') +param enabled bool = true + +@description('Optional. The severity of the alert.') +@allowed([ + 0 + 1 + 2 + 3 + 4 +]) +param severity int = 3 + +@description('Optional. how often the metric alert is evaluated represented in ISO 8601 duration format.') +@allowed([ + 'PT1M' + 'PT5M' + 'PT15M' + 'PT30M' + 'PT1H' +]) +param evaluationFrequency string = 'PT5M' + +@description('Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold.') +@allowed([ + 'PT1M' + 'PT5M' + 'PT15M' + 'PT30M' + 'PT1H' + 'PT6H' + 'PT12H' + 'P1D' +]) +param windowSize string = 'PT15M' + +@description('Optional. the list of resource IDs that this metric alert is scoped to.') +param scopes array = [ + subscription().id +] + +@description('Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') +param targetResourceType string = '' + +@description('Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') +param targetResourceRegion string = '' + +@description('Optional. The flag that indicates whether the alert should be auto resolved or not.') +param autoMitigate bool = true + +@description('Optional. The list of actions to take when alert triggers.') +param actions array = [] + +@description('Optional. Maps to the \'odata.type\' field. Specifies the type of the alert criteria.') +@allowed([ + 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' +]) +param alertCriteriaType string = 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + +@description('Required. Criterias to trigger the alert. Array of \'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria\' or \'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria\' objects.') +param criterias array + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var actionGroups = [for action in actions: { + actionGroupId: contains(action, 'actionGroupId') ? action.actionGroupId : action + webHookProperties: contains(action, 'webHookProperties') ? action.webHookProperties : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = { + name: name + location: location + tags: tags + properties: { + description: alertDescription + severity: severity + enabled: enabled + scopes: scopes + evaluationFrequency: evaluationFrequency + windowSize: windowSize + targetResourceType: targetResourceType + targetResourceRegion: targetResourceRegion + criteria: { + 'odata.type': any(alertCriteriaType) + allOf: criterias + } + autoMitigate: autoMitigate + actions: actionGroups + } +} + +module metricAlert_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-MetricAlert-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: metricAlert.id + } +}] + +@description('The resource group the metric alert was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the metric alert.') +output name string = metricAlert.name + +@description('The resource ID of the metric alert.') +output resourceId string = metricAlert.id + +@description('The location the resource was deployed into.') +output location string = metricAlert.location diff --git a/modules/Microsoft.Insights/metricAlerts/readme.md b/modules/Microsoft.Insights/metricAlerts/readme.md new file mode 100644 index 0000000..48ec87d --- /dev/null +++ b/modules/Microsoft.Insights/metricAlerts/readme.md @@ -0,0 +1,493 @@ +# Metric Alerts `[Microsoft.Insights/metricAlerts]` + +This module deploys an alert based on metrics. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/metricAlerts` | [2018-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2018-03-01/metricAlerts) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `criterias` | array | Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects. | +| `name` | string | The name of the alert. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `targetResourceRegion` | string | `''` | The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria. | +| `targetResourceType` | string | `''` | The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | | The list of actions to take when alert triggers. | +| `alertCriteriaType` | string | `'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'` | `[Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria, Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria, Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria]` | Maps to the 'odata.type' field. Specifies the type of the alert criteria. | +| `alertDescription` | string | `''` | | Description of the alert. | +| `autoMitigate` | bool | `True` | | The flag that indicates whether the alert should be auto resolved or not. | +| `enabled` | bool | `True` | | Indicates whether this alert is enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `evaluationFrequency` | string | `'PT5M'` | `[PT15M, PT1H, PT1M, PT30M, PT5M]` | how often the metric alert is evaluated represented in ISO 8601 duration format. | +| `location` | string | `'global'` | | Location for all resources. | +| `roleAssignments` | array | `[]` | | 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'. | +| `scopes` | array | `[[subscription().id]]` | | the list of resource IDs that this metric alert is scoped to. | +| `severity` | int | `3` | `[0, 1, 2, 3, 4]` | The severity of the alert. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `windowSize` | string | `'PT15M'` | `[P1D, PT12H, PT15M, PT1H, PT1M, PT30M, PT5M, PT6H]` | the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold. | + + +### Parameter Usage: actions + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + { + "actionGroupId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/ActionGroupName", + "webhookProperties": {} + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + { + actionGroupId: '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/ActionGroupName' + webhookProperties: {} + } +] +``` + +
+

+ +`webhookProperties` is optional. + +If you do only want to provide actionGroupIds, a shorthand use of the parameter is available. + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName" + ] +} +``` + +
+ + +
+ +Bicep format + +```bicep + + +``` + +
+ +### Parameter Usage: `criteria` + +**SingleResourceMultipleMetricCriteria** + + +
+ +Parameter JSON format + +```json +{ + "criterionType": "string", + "dimensions": [], + "metricName": "string", + "metricNamespace": "string", + "name": "string", + "operator": "string", + "threshold": "integer", + "timeAggregation": "string" +} +``` + +
+ + +
+ +Bicep format + +```bicep +{ + criterionType: 'string' + dimensions: [] + metricName: 'string' + metricNamespace: 'string' + name: 'string' + operator: 'string' + threshold: 'integer' + timeAggregation: 'string' +} +``` + +
+

+ +**MultipleResourceMultipleMetricCriteria** + +

+ +Parameter JSON format + +```json +{ + "criterionType": "string", + "dimensions": [], + "metricName": "string", + "metricNamespace": "string", + "name": "string", + "operator": "string", + "threshold": "integer", + "timeAggregation": "string", + "alertSensitivity": "string", + "failingPeriods": { + "minFailingPeriodsToAlert": "integer", + "numberOfEvaluationPeriods": "integer" + }, + "ignoreDataBefore": "string" +} +``` + +
+ + +
+ +Bicep format + +```bicep +{ + criterionType: 'string' + dimensions: [] + metricName: 'string' + metricNamespace: 'string' + name: 'string' + operator: 'string' + threshold: 'integer' + timeAggregation: 'string' + alertSensitivity: 'string' + failingPeriods: { + minFailingPeriodsToAlert: 'integer' + numberOfEvaluationPeriods: 'integer' + } + ignoreDataBefore: 'string' +} +``` + +
+

+ +**Sample** +The following sample can be use both for Single and Multiple criteria. The other parameters are optional. + +

+ +Parameter JSON format + +```json +"criterias":{ + "value": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Additional notes on parameters + +- When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory (see above) +- MultipleResourceMultipleMetricCriteria is suggested, as additional scopes can be added later +- It's not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. Delete and re-create the alert. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the metric alert. | +| `resourceGroupName` | string | The resource group the metric alert was deployed into. | +| `resourceId` | string | The resource ID of the metric alert. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module metricAlerts './Microsoft.Insights/metricAlerts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-MetricAlerts' + params: { + // Required parameters + criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + name: '<>-az-ma-x-001' + // Non-required parameters + actions: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001' + ] + alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + targetResourceRegion: 'westeurope' + targetResourceType: 'microsoft.compute/virtualmachines' + windowSize: 'PT15M' + } +} +``` + +
+

+ +

+ +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 + "criterias": { + "value": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ] + }, + "name": { + "value": "<>-az-ma-x-001" + }, + // Non-required parameters + "actions": { + "value": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/microsoft.insights/actiongroups/adp-<>-az-ag-x-001" + ] + }, + "alertCriteriaType": { + "value": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "targetResourceRegion": { + "value": "westeurope" + }, + "targetResourceType": { + "value": "microsoft.compute/virtualmachines" + }, + "windowSize": { + "value": "PT15M" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/metricAlerts/version.json b/modules/Microsoft.Insights/metricAlerts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/metricAlerts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d9ca09c --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 privateLinkScope 'Microsoft.Insights/privateLinkScopes@2019-10-17-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateLinkScope.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateLinkScope +}] diff --git a/modules/Microsoft.Insights/privateLinkScopes/.test/min.parameters.json b/modules/Microsoft.Insights/privateLinkScopes/.test/min.parameters.json new file mode 100644 index 0000000..a976e13 --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/.test/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": "<>-az-pls-min-001" + } + } +} diff --git a/modules/Microsoft.Insights/privateLinkScopes/.test/parameters.json b/modules/Microsoft.Insights/privateLinkScopes/.test/parameters.json new file mode 100644 index 0000000..01eaf54 --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/.test/parameters.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pls-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "scopedResources": { + "value": [ + { + "name": "scoped1", + "linkedResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "azuremonitor", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.monitor.azure.com" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/privateLinkScopes/deploy.bicep b/modules/Microsoft.Insights/privateLinkScopes/deploy.bicep new file mode 100644 index 0000000..9624a9b --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/deploy.bicep @@ -0,0 +1,114 @@ +@description('Required. Name of the private link scope.') +@minLength(1) +param name string + +@description('Optional. The location of the private link scope. Should be global.') +param location string = 'global' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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 Azure Monitor Resources.') +param scopedResources array = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkScope 'Microsoft.Insights/privateLinkScopes@2019-10-17-preview' = { + name: name + location: location + tags: tags + properties: {} +} + +module privateLinkScope_scopedResource 'scopedResources/deploy.bicep' = [for (scopedResource, index) in scopedResources: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-ScopedRes-${index}' + params: { + name: scopedResource.name + privateLinkScopeName: privateLinkScope.name + linkedResourceId: scopedResource.linkedResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource privateLinkScope_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${privateLinkScope.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkScope +} + +module privateLinkScope_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(privateLinkScope.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: privateLinkScope.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module privateLinkScope_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateLinkScope.id + } +}] + +@description('The name of the private link scope.') +output name string = privateLinkScope.name + +@description('The resource ID of the private link scope.') +output resourceId string = privateLinkScope.id + +@description('The resource group the private link scope was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = privateLinkScope.location diff --git a/modules/Microsoft.Insights/privateLinkScopes/readme.md b/modules/Microsoft.Insights/privateLinkScopes/readme.md new file mode 100644 index 0000000..2951b53 --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/readme.md @@ -0,0 +1,384 @@ +# Azure Monitor Private Link Scopes `[Microsoft.Insights/privateLinkScopes]` + +This module deploys an Azure Monitor Private Link Scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `microsoft.insights/privateLinkScopes` | [2019-10-17-preview](https://docs.microsoft.com/en-us/azure/templates/microsoft.insights/2019-10-17-preview/privateLinkScopes) | +| `Microsoft.Insights/privateLinkScopes/scopedResources` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-07-01-preview/privateLinkScopes/scopedResources) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the private link scope. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `'global'` | | The location of the private link scope. Should be global. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `roleAssignments` | array | `[]` | | 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'. | +| `scopedResources` | _[scopedResources](scopedResources/readme.md)_ array | `[]` | | Configuration details for Azure Monitor Resources. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private link scope. | +| `resourceGroupName` | string | The resource group the private link scope was deployed into. | +| `resourceId` | string | The resource ID of the private link scope. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module privateLinkScopes './Microsoft.Insights/privateLinkScopes/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkScopes' + params: { + name: '<>-az-pls-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pls-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module privateLinkScopes './Microsoft.Insights/privateLinkScopes/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkScopes' + params: { + // Required parameters + name: '<>-az-pls-x-001' + // Non-required parameters + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.monitor.azure.com' + ] + } + service: 'azuremonitor' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + scopedResources: [ + { + linkedResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + name: 'scoped1' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-pls-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.monitor.azure.com" + ] + }, + "service": "azuremonitor", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopedResources": { + "value": [ + { + "linkedResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "name": "scoped1" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep new file mode 100644 index 0000000..dda15be --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep @@ -0,0 +1,46 @@ +@description('Required. Name of the private link scoped resource.') +@minLength(1) +param name string + +@description('Conditional. The name of the parent private link scope. Required if the template is used in a standalone deployment.') +@minLength(1) +param privateLinkScopeName string + +@description('Required. The resource ID of the scoped Azure monitor resource.') +param linkedResourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-preview' existing = { + name: privateLinkScopeName +} + +resource scopedResource 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = { + name: name + parent: privateLinkScope + properties: { + linkedResourceId: linkedResourceId + } +} + +@description('The name of the resource group where the resource has been deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the deployed scopedResource.') +output resourceId string = scopedResource.id + +@description('The full name of the deployed Scoped Resource.') +output name string = scopedResource.name diff --git a/modules/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md new file mode 100644 index 0000000..c8dafec --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md @@ -0,0 +1,47 @@ +# Insights PrivateLinkScopes ScopedResources `[Microsoft.Insights/privateLinkScopes/scopedResources]` + +This module deploys Insights PrivateLinkScopes ScopedResources. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/privateLinkScopes/scopedResources` | [2021-07-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-07-01-preview/privateLinkScopes/scopedResources) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `linkedResourceId` | string | The resource ID of the scoped Azure monitor resource. | +| `name` | string | Name of the private link scoped resource. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateLinkScopeName` | string | The name of the parent private link scope. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The full name of the deployed Scoped Resource. | +| `resourceGroupName` | string | The name of the resource group where the resource has been deployed. | +| `resourceId` | string | The resource ID of the deployed scopedResource. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Insights/privateLinkScopes/scopedResources/version.json b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/scopedResources/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/privateLinkScopes/version.json b/modules/Microsoft.Insights/privateLinkScopes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/privateLinkScopes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..f547d41 --- /dev/null +++ b/modules/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + '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 queryAlert 'microsoft.insights/scheduledQueryRules@2018-04-16' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(queryAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: queryAlert +}] diff --git a/modules/Microsoft.Insights/scheduledQueryRules/.test/parameters.json b/modules/Microsoft.Insights/scheduledQueryRules/.test/parameters.json new file mode 100644 index 0000000..85cb876 --- /dev/null +++ b/modules/Microsoft.Insights/scheduledQueryRules/.test/parameters.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "myAlert01" + }, + "alertDescription": { + "value": "My sample Alert" + }, + "scopes": { + "value": [ + "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + ] + }, + "evaluationFrequency": { + "value": "PT5M" + }, + "windowSize": { + "value": "PT5M" + }, + "suppressForMinutes": { + "value": "PT5M" + }, + "queryTimeRange": { + "value": "PT5M" + }, + "autoMitigate": { + "value": false + }, + "criterias": { + "value": { + "allOf": [ + { + "query": "Perf | where ObjectName == \"LogicalDisk\" | where CounterName == \"% Free Space\" | where InstanceName <> \"HarddiskVolume1\" and InstanceName <> \"_Total\" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)", + "timeAggregation": "Average", + "metricMeasureColumn": "AggregatedValue", + "dimensions": [ + { + "name": "Computer", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "InstanceName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThan", + "threshold": 0 + } + ] + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Insights/scheduledQueryRules/deploy.bicep b/modules/Microsoft.Insights/scheduledQueryRules/deploy.bicep new file mode 100644 index 0000000..325eaa0 --- /dev/null +++ b/modules/Microsoft.Insights/scheduledQueryRules/deploy.bicep @@ -0,0 +1,131 @@ +@description('Required. The name of the Alert.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The description of the scheduled query rule.') +param alertDescription string = '' + +@description('Optional. The flag which indicates whether this scheduled query rule is enabled.') +param enabled bool = true + +@description('Optional. Indicates the type of scheduled query rule.') +@allowed([ + 'LogAlert' + 'LogToMetric' +]) +param kind string = 'LogAlert' + +@description('Optional. The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert.') +param autoMitigate bool = true + +@description('Optional. If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert.') +param queryTimeRange string = '' + +@description('Optional. The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert.') +param skipQueryValidation bool = false + +@description('Optional. List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert.') +param targetResourceTypes array = [] + +@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('Required. The list of resource IDs that this scheduled query rule is scoped to.') +param scopes array = [] + +@description('Optional. Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert.') +@allowed([ + 0 + 1 + 2 + 3 + 4 +]) +param severity int = 3 + +@description('Optional. How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert.') +param evaluationFrequency string = '' + +@description('Optional. The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert.') +param windowSize string = '' + +@description('Optional. Actions to invoke when the alert fires.') +param actions array = [] + +@description('Optional. The rule criteria that defines the conditions of the scheduled query rule.') +param criterias object = {} + +@description('Optional. Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert.') +param suppressForMinutes string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource queryRule 'Microsoft.Insights/scheduledQueryRules@2021-02-01-preview' = { + name: name + location: location + tags: tags + kind: kind + properties: { + actions: { + actionGroups: actions + customProperties: {} + } + autoMitigate: (kind == 'LogAlert') ? autoMitigate : null + criteria: criterias + + description: alertDescription + displayName: name + enabled: enabled + evaluationFrequency: (kind == 'LogAlert' && !empty(evaluationFrequency)) ? evaluationFrequency : null + muteActionsDuration: (kind == 'LogAlert' && !empty(suppressForMinutes)) ? suppressForMinutes : null + overrideQueryTimeRange: (kind == 'LogAlert' && !empty(queryTimeRange)) ? queryTimeRange : null + scopes: scopes + severity: (kind == 'LogAlert') ? severity : null + skipQueryValidation: (kind == 'LogAlert') ? skipQueryValidation : null + targetResourceTypes: (kind == 'LogAlert') ? targetResourceTypes : null + windowSize: (kind == 'LogAlert' && !empty(windowSize)) ? windowSize : null + } +} + +module queryRule_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-QueryRule-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: queryRule.id + } +}] + +@description('The Name of the created query rule.') +output name string = queryRule.name + +@description('The resource ID of the created query rule.') +output resourceId string = queryRule.id + +@description('The Resource Group of the created query rule.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = queryRule.location diff --git a/modules/Microsoft.Insights/scheduledQueryRules/readme.md b/modules/Microsoft.Insights/scheduledQueryRules/readme.md new file mode 100644 index 0000000..e61face --- /dev/null +++ b/modules/Microsoft.Insights/scheduledQueryRules/readme.md @@ -0,0 +1,315 @@ +# Scheduled Query Rules `[Microsoft.Insights/scheduledQueryRules]` + +This module deploys a scheduled query rule. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/scheduledQueryRules` | [2021-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-02-01-preview/scheduledQueryRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Alert. | +| `scopes` | array | The list of resource IDs that this scheduled query rule is scoped to. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | | Actions to invoke when the alert fires. | +| `alertDescription` | string | `''` | | The description of the scheduled query rule. | +| `autoMitigate` | bool | `True` | | The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert. | +| `criterias` | object | `{object}` | | The rule criteria that defines the conditions of the scheduled query rule. | +| `enabled` | bool | `True` | | The flag which indicates whether this scheduled query rule is enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `evaluationFrequency` | string | `''` | | How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert. | +| `kind` | string | `'LogAlert'` | `[LogAlert, LogToMetric]` | Indicates the type of scheduled query rule. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `queryTimeRange` | string | `''` | | If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert. | +| `roleAssignments` | array | `[]` | | 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'. | +| `severity` | int | `3` | `[0, 1, 2, 3, 4]` | Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert. | +| `skipQueryValidation` | bool | `False` | | The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert. | +| `suppressForMinutes` | string | `''` | | Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `targetResourceTypes` | array | `[]` | | List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert. | +| `windowSize` | string | `''` | | The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the created query rule. | +| `resourceGroupName` | string | The Resource Group of the created query rule. | +| `resourceId` | string | The resource ID of the created query rule. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module scheduledQueryRules './Microsoft.Insights/scheduledQueryRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ScheduledQueryRules' + params: { + // Required parameters + name: 'myAlert01' + // Non-required parameters + alertDescription: 'My sample Alert' + autoMitigate: false + criterias: { + allOf: [ + { + dimensions: [ + { + name: 'Computer' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'InstanceName' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricMeasureColumn: 'AggregatedValue' + operator: 'GreaterThan' + query: 'Perf | where ObjectName == \'LogicalDisk\' | where CounterName == \'% Free Space\' | where InstanceName <> \'HarddiskVolume1\' and InstanceName <> \'_Total\' | summarize AggregatedValue = min(CounterValue) by Computer InstanceName bin(TimeGenerated5m)' + threshold: 0 + timeAggregation: 'Average' + } + ] + } + evaluationFrequency: 'PT5M' + queryTimeRange: 'PT5M' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + scopes: [ + '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + ] + suppressForMinutes: 'PT5M' + windowSize: 'PT5M' + } +} +``` + +
+

+ +

+ +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": "myAlert01" + }, + // Non-required parameters + "alertDescription": { + "value": "My sample Alert" + }, + "autoMitigate": { + "value": false + }, + "criterias": { + "value": { + "allOf": [ + { + "dimensions": [ + { + "name": "Computer", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "InstanceName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "metricMeasureColumn": "AggregatedValue", + "operator": "GreaterThan", + "query": "Perf | where ObjectName == \"LogicalDisk\" | where CounterName == \"% Free Space\" | where InstanceName <> \"HarddiskVolume1\" and InstanceName <> \"_Total\" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)", + "threshold": 0, + "timeAggregation": "Average" + } + ] + } + }, + "evaluationFrequency": { + "value": "PT5M" + }, + "queryTimeRange": { + "value": "PT5M" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopes": { + "value": [ + "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + ] + }, + "suppressForMinutes": { + "value": "PT5M" + }, + "windowSize": { + "value": "PT5M" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Insights/scheduledQueryRules/version.json b/modules/Microsoft.Insights/scheduledQueryRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Insights/scheduledQueryRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.KeyVault/vaults/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.KeyVault/vaults/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..7ba24f9 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,77 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Crypto Service Encryption User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') + 'Key Vault Crypto User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Key Vault Secrets User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6') + '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 keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(keyVault.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: keyVault +}] diff --git a/modules/Microsoft.KeyVault/vaults/.test/common/dependencies.bicep b/modules/Microsoft.KeyVault/vaults/.test/common/dependencies.bicep new file mode 100644 index 0000000..5119944 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/.test/common/dependencies.bicep @@ -0,0 +1,63 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/24' + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: '10.0.0.0/24' + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSResourceId string = privateDNSZone.id diff --git a/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep new file mode 100644 index 0000000..99ecd3a --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep @@ -0,0 +1,161 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes') +@maxLength(90) +param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to') +param location 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 = 'kvvcom' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/dependencyConstructs/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + accessPolicies: [ + { + objectId: resourceGroupResources.outputs.managedIdentityPrincipalId + permissions: { + keys: [ + 'get' + 'list' + 'update' + ] + secrets: [ + 'all' + ] + } + tenantId: tenant().tenantId + } + { + objectId: resourceGroupResources.outputs.managedIdentityPrincipalId + permissions: { + certificates: [ + 'backup' + 'create' + 'delete' + ] + secrets: [ + 'all' + ] + } + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + enableRbacAuthorization: false + keys: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + name: 'keyName' + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + lock: 'CanNotDelete' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [] + virtualNetworkRules: [ + { + action: 'Allow' + id: resourceGroupResources.outputs.subnetResourceId + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + resourceGroupResources.outputs.privateDNSResourceId + ] + } + service: 'vault' + subnetResourceId: resourceGroupResources.outputs.subnetResourceId + } + ] + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + secrets: { + secureList: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + contentType: 'Something' + name: 'secretName' + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + value: 'secretValue' + } + ] + } + softDeleteRetentionInDays: 7 + } +} diff --git a/modules/Microsoft.KeyVault/vaults/.test/min/deploy.test.bicep b/modules/Microsoft.KeyVault/vaults/.test/min/deploy.test.bicep new file mode 100644 index 0000000..49af49c --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/.test/min/deploy.test.bicep @@ -0,0 +1,37 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes') +@maxLength(90) +param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to') +param location 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 = 'kvvmin' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + } +} diff --git a/modules/Microsoft.KeyVault/vaults/accessPolicies/deploy.bicep b/modules/Microsoft.KeyVault/vaults/accessPolicies/deploy.bicep new file mode 100644 index 0000000..e161410 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/accessPolicies/deploy.bicep @@ -0,0 +1,51 @@ +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Optional. The access policy deployment.') +param name string = 'add' + +@description('Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault\'s tenant ID.') +param accessPolicies array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var formattedAccessPolicies = [for accessPolicy in accessPolicies: { + applicationId: contains(accessPolicy, 'applicationId') ? accessPolicy.applicationId : '' + objectId: contains(accessPolicy, 'objectId') ? accessPolicy.objectId : '' + permissions: accessPolicy.permissions + tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = { + name: keyVaultName +} + +resource policies 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = { + name: name + parent: keyVault + properties: { + accessPolicies: formattedAccessPolicies + } +} + +@description('The name of the resource group the access policies assignment was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the access policies assignment.') +output name string = policies.name + +@description('The resource ID of the access policies assignment.') +output resourceId string = policies.id diff --git a/modules/Microsoft.KeyVault/vaults/accessPolicies/readme.md b/modules/Microsoft.KeyVault/vaults/accessPolicies/readme.md new file mode 100644 index 0000000..a656d8d --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/accessPolicies/readme.md @@ -0,0 +1,102 @@ +# Key Vault Access Policies `[Microsoft.KeyVault/vaults/accessPolicies]` + +This module deploys key vault access policies. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2021-06-01-preview/vaults/accessPolicies) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `accessPolicies` | array | `[]` | An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'add'` | The access policy deployment. | + + +### Parameter Usage: `accessPolicies` + +

+ +Parameter JSON format + +```json +"accessPolicies": { + "value": [ + { + "tenantId": null, // Optional + "applicationId": null, // Optional + "objectId": null, + "permissions": { + "certificates": [ + "All" + ], + "keys": [ + "All" + ], + "secrets": [ + "All" + ] + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +accessPolicies: [ + { + tenantId: null // Optional + applicationId: null // Optional + objectId: null + permissions: { + certificates: [ + 'All' + ] + keys: [ + 'All' + ] + secrets: [ + 'All' + ] + } + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the access policies assignment. | +| `resourceGroupName` | string | The name of the resource group the access policies assignment was created in. | +| `resourceId` | string | The resource ID of the access policies assignment. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.KeyVault/vaults/accessPolicies/version.json b/modules/Microsoft.KeyVault/vaults/accessPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/accessPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.KeyVault/vaults/deploy.bicep b/modules/Microsoft.KeyVault/vaults/deploy.bicep new file mode 100644 index 0000000..85a848e --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/deploy.bicep @@ -0,0 +1,326 @@ +// ================ // +// Parameters // +// ================ // +@description('Required. Name of the Key Vault. Must be globally unique.') +@maxLength(24) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array of access policies object.') +param accessPolicies array = [] + +@description('Optional. All secrets to create.') +@secure() +param secrets object = {} + +@description('Optional. All keys to create.') +param keys array = [] + +@description('Optional. Specifies if the vault is enabled for deployment by script or compute.') +@allowed([ + true + false +]) +param enableVaultForDeployment bool = true + +@description('Optional. Specifies if the vault is enabled for a template deployment.') +@allowed([ + true + false +]) +param enableVaultForTemplateDeployment bool = true + +@description('Optional. Specifies if the azure platform has access to the vault for enabling disk encryption scenarios.') +@allowed([ + true + false +]) +param enableVaultForDiskEncryption bool = true + +@description('Optional. Switch to enable/disable Key Vault\'s soft delete feature.') +param enableSoftDelete bool = true + +@description('Optional. softDelete data retention days. It accepts >=7 and <=90.') +param softDeleteRetentionInDays int = 90 + +@description('Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC.') +param enableRbacAuthorization bool = false + +@description('Optional. The vault\'s create mode to indicate whether the vault need to be recovered or not. - recover or default.') +param createMode string = 'default' + +@description('Optional. Provide \'true\' to enable Key Vault\'s purge protection feature.') +param enablePurgeProtection bool = false + +@description('Optional. Specifies the SKU for the vault.') +@allowed([ + 'premium' + 'standard' +]) +param vaultSku string = 'premium' + +@description('Optional. Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny.') +param networkAcls object = {} + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'AuditEvent' + 'AzurePolicyEvaluationDetails' +]) +param diagnosticLogCategoriesToEnable array = [ + 'AuditEvent' + 'AzurePolicyEvaluationDetails' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +// =========== // +// Variables // +// =========== // +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var networkAcls_var = { + bypass: !empty(networkAcls) ? networkAcls.bypass : null + defaultAction: !empty(networkAcls) ? networkAcls.defaultAction : null + virtualNetworkRules: (!empty(networkAcls) && contains(networkAcls, 'virtualNetworkRules')) ? networkAcls.virtualNetworkRules : [] + ipRules: (!empty(networkAcls) && contains(networkAcls, 'ipRules')) ? networkAcls.ipRules : [] +} + +var formattedAccessPolicies = [for accessPolicy in accessPolicies: { + applicationId: contains(accessPolicy, 'applicationId') ? accessPolicy.applicationId : '' + objectId: contains(accessPolicy, 'objectId') ? accessPolicy.objectId : '' + permissions: accessPolicy.permissions + tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId +}] + +var secretList = !empty(secrets) ? secrets.secureList : [] + +var enableReferencedModulesTelemetry = false + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = { + name: name + location: location + tags: tags + properties: { + enabledForDeployment: enableVaultForDeployment + enabledForTemplateDeployment: enableVaultForTemplateDeployment + enabledForDiskEncryption: enableVaultForDiskEncryption + enableSoftDelete: enableSoftDelete + softDeleteRetentionInDays: softDeleteRetentionInDays + enableRbacAuthorization: enableRbacAuthorization + createMode: createMode + enablePurgeProtection: enablePurgeProtection ? enablePurgeProtection : null + tenantId: subscription().tenantId + accessPolicies: formattedAccessPolicies + sku: { + name: vaultSku + family: 'A' + } + networkAcls: !empty(networkAcls) ? networkAcls_var : null + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : null) + } +} + +resource keyVault_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${keyVault.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: keyVault +} + +resource keyVault_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: keyVault +} + +module keyVault_accessPolicies 'accessPolicies/deploy.bicep' = if (!empty(accessPolicies)) { + name: '${uniqueString(deployment().name, location)}-KeyVault-AccessPolicies' + params: { + keyVaultName: keyVault.name + accessPolicies: formattedAccessPolicies + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module keyVault_secrets 'secrets/deploy.bicep' = [for (secret, index) in secretList: { + name: '${uniqueString(deployment().name, location)}-KeyVault-Secret-${index}' + params: { + name: secret.name + value: secret.value + keyVaultName: keyVault.name + attributesEnabled: contains(secret, 'attributesEnabled') ? secret.attributesEnabled : true + attributesExp: contains(secret, 'attributesExp') ? secret.attributesExp : -1 + attributesNbf: contains(secret, 'attributesNbf') ? secret.attributesNbf : -1 + contentType: contains(secret, 'contentType') ? secret.contentType : '' + tags: contains(secret, 'tags') ? secret.tags : {} + roleAssignments: contains(secret, 'roleAssignments') ? secret.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module keyVault_keys 'keys/deploy.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-KeyVault-Key-${index}' + params: { + name: key.name + keyVaultName: keyVault.name + attributesEnabled: contains(key, 'attributesEnabled') ? key.attributesEnabled : true + attributesExp: contains(key, 'attributesExp') ? key.attributesExp : -1 + attributesNbf: contains(key, 'attributesNbf') ? key.attributesNbf : -1 + curveName: contains(key, 'curveName') ? key.curveName : 'P-256' + keyOps: contains(key, 'keyOps') ? key.keyOps : [] + keySize: contains(key, 'keySize') ? key.keySize : -1 + kty: contains(key, 'kty') ? key.kty : 'EC' + tags: contains(key, 'tags') ? key.tags : {} + roleAssignments: contains(key, 'roleAssignments') ? key.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module keyVault_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-KeyVault-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(keyVault.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: keyVault.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module keyVault_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-KeyVault-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: keyVault.id + } +}] + +// =========== // +// Outputs // +// =========== // +@description('The resource ID of the key vault.') +output resourceId string = keyVault.id + +@description('The name of the resource group the key vault was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the key vault.') +output name string = keyVault.name + +@description('The URI of the key vault.') +output uri string = keyVault.properties.vaultUri + +@description('The location the resource was deployed into.') +output location string = keyVault.location diff --git a/modules/Microsoft.KeyVault/vaults/keys/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.KeyVault/vaults/keys/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ae461b6 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/keys/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Crypto Service Encryption User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') + 'Key Vault Crypto User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + '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 key 'Microsoft.KeyVault/vaults/keys@2021-06-01-preview' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(key.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: key +}] diff --git a/modules/Microsoft.KeyVault/vaults/keys/deploy.bicep b/modules/Microsoft.KeyVault/vaults/keys/deploy.bicep new file mode 100644 index 0000000..71e620d --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/keys/deploy.bicep @@ -0,0 +1,111 @@ +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Required. The name of the key.') +param name string + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Determines whether the object is enabled.') +param attributesEnabled bool = true + +@description('Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible.') +param attributesExp int = -1 + +@description('Optional. Not before date in seconds since 1970-01-01T00:00:00Z.') +param attributesNbf int = -1 + +@description('Optional. The elliptic curve name.') +@allowed([ + 'P-256' + 'P-256K' + 'P-384' + 'P-521' +]) +param curveName string = 'P-256' + +@description('Optional. Array of JsonWebKeyOperation.') +@allowed([ + 'decrypt' + 'encrypt' + 'import' + 'sign' + 'unwrapKey' + 'verify' + 'wrapKey' +]) +param keyOps array = [] + +@description('Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA.') +param keySize int = -1 + +@description('Optional. The type of the key.') +@allowed([ + 'EC' + 'EC-HSM' + 'RSA' + 'RSA-HSM' +]) +param kty string = 'EC' + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = { + name: keyVaultName +} + +resource key 'Microsoft.KeyVault/vaults/keys@2019-09-01' = { + name: name + parent: keyVault + tags: tags + properties: { + attributes: { + enabled: attributesEnabled + exp: attributesExp != -1 ? attributesExp : null + nbf: attributesNbf != -1 ? attributesNbf : null + } + curveName: curveName + keyOps: keyOps + keySize: keySize != -1 ? keySize : null + kty: kty + } +} + +module key_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: key.id + } +}] + +@description('The name of the key.') +output name string = key.name + +@description('The resource ID of the key.') +output resourceId string = key.id + +@description('The name of the resource group the key was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.KeyVault/vaults/keys/readme.md b/modules/Microsoft.KeyVault/vaults/keys/readme.md new file mode 100644 index 0000000..6d4770e --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/keys/readme.md @@ -0,0 +1,156 @@ +# Key Vault Key `[Microsoft.KeyVault/vaults/keys]` + +This module deploys a key vault key. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.KeyVault/vaults/keys` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2019-09-01/vaults/keys) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `attributesEnabled` | bool | `True` | | Determines whether the object is enabled. | +| `attributesExp` | int | `-1` | | Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible. | +| `attributesNbf` | int | `-1` | | Not before date in seconds since 1970-01-01T00:00:00Z. | +| `curveName` | string | `'P-256'` | `[P-256, P-256K, P-384, P-521]` | The elliptic curve name. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `keyOps` | array | `[]` | `[decrypt, encrypt, import, sign, unwrapKey, verify, wrapKey]` | Array of JsonWebKeyOperation. | +| `keySize` | int | `-1` | | The key size in bits. For example: 2048, 3072, or 4096 for RSA. | +| `kty` | string | `'EC'` | `[EC, EC-HSM, RSA, RSA-HSM]` | The type of the key. | +| `roleAssignments` | array | `[]` | | 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}` | | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. | +| `resourceGroupName` | string | The name of the resource group the key was created in. | +| `resourceId` | string | The resource ID of the key. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.KeyVault/vaults/keys/version.json b/modules/Microsoft.KeyVault/vaults/keys/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/keys/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.KeyVault/vaults/readme.md b/modules/Microsoft.KeyVault/vaults/readme.md new file mode 100644 index 0000000..8b1366e --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/readme.md @@ -0,0 +1,702 @@ +# Key Vaults `[Microsoft.KeyVault/vaults]` + +This module deploys a key vault and its child resources. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults` | [2021-11-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2021-11-01-preview/vaults) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2021-06-01-preview/vaults/accessPolicies) | +| `Microsoft.KeyVault/vaults/keys` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2019-09-01/vaults/keys) | +| `Microsoft.KeyVault/vaults/secrets` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2019-09-01/vaults/secrets) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Key Vault. Must be globally unique. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `accessPolicies` | _[accessPolicies](accessPolicies/readme.md)_ array | `[]` | | Array of access policies object. | +| `createMode` | string | `'default'` | | The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticLogCategoriesToEnable` | array | `[AuditEvent, AzurePolicyEvaluationDetails]` | `[AuditEvent, AzurePolicyEvaluationDetails]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enablePurgeProtection` | bool | `False` | | Provide 'true' to enable Key Vault's purge protection feature. | +| `enableRbacAuthorization` | bool | `False` | | Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored (warning: this is a preview feature). When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. If null or not specified, the vault is created with the default value of false. Note that management actions are always authorized with RBAC. | +| `enableSoftDelete` | bool | `True` | | Switch to enable/disable Key Vault's soft delete feature. | +| `enableVaultForDeployment` | bool | `True` | `[False, True]` | Specifies if the vault is enabled for deployment by script or compute. | +| `enableVaultForDiskEncryption` | bool | `True` | `[False, True]` | Specifies if the azure platform has access to the vault for enabling disk encryption scenarios. | +| `enableVaultForTemplateDeployment` | bool | `True` | `[False, True]` | Specifies if the vault is enabled for a template deployment. | +| `keys` | _[keys](keys/readme.md)_ array | `[]` | | All keys to create. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkAcls` | object | `{object}` | | Service endpoint object information. For security reasons, it is recommended to set the DefaultAction Deny. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | 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'. | +| `secrets` | secureObject | `{object}` | | All secrets to create. | +| `softDeleteRetentionInDays` | int | `90` | | softDelete data retention days. It accepts >=7 and <=90. | +| `tags` | object | `{object}` | | Resource tags. | +| `vaultSku` | string | `'premium'` | `[premium, standard]` | Specifies the SKU for the vault. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `networkAcls` + +

+ +Parameter JSON format + +```json +"networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "virtualNetworkRules": [ + { + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001" + } + ], + "ipRules": [] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + } + ] + ipRules: [] +} +``` + +
+

+ +### Parameter Usage: `vNetId` + +

+ +Parameter JSON format + +```json +"vNetId": { + "value": "/subscriptions/00000000/resourceGroups/resourceGroup" +} +``` + +
+ +
+ +Bicep format + +```bicep +vNetId: '/subscriptions/00000000/resourceGroups/resourceGroup' +``` + +
+

+ +### Parameter Usage: `accessPolicies` + +

+ +Parameter JSON format + +```json +"accessPolicies": { + "value": [ + { + "tenantId": null, // Optional + "applicationId": null, // Optional + "objectId": null, + "permissions": { + "certificates": [ + "All" + ], + "keys": [ + "All" + ], + "secrets": [ + "All" + ] + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +accessPolicies: [ + { + tenantId: null // Optional + applicationId: null // Optional + objectId: null + permissions: { + certificates: [ + 'All' + ] + keys: [ + 'All' + ] + secrets: [ + 'All' + ] + } + } +] +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the key vault. | +| `resourceGroupName` | string | The name of the resource group the key vault was created in. | +| `resourceId` | string | The resource ID of the key vault. | +| `uri` | string | The URI of the key vault. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module vaults './Microsoft.KeyVault/vaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-kvvcom' + params: { + name: '<>kvvcom001' + accessPolicies: [ + { + objectId: '' + permissions: { + keys: [ + 'get' + 'list' + 'update' + ] + secrets: [ + 'all' + ] + } + tenantId: '' + } + { + objectId: '' + permissions: { + certificates: [ + 'backup' + 'create' + 'delete' + ] + secrets: [ + 'all' + ] + } + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableRbacAuthorization: false + keys: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + name: 'keyName' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + lock: 'CanNotDelete' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [] + virtualNetworkRules: [ + { + action: 'Allow' + id: '' + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '' + ] + } + service: 'vault' + subnetResourceId: '' + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + secrets: { + secureList: [ + { + attributesExp: 1702648632 + attributesNbf: 10000 + contentType: 'Something' + name: 'secretName' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + value: 'secretValue' + } + ] + } + softDeleteRetentionInDays: 7 + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>kvvcom001" + }, + "accessPolicies": { + "value": [ + { + "objectId": "", + "permissions": { + "keys": [ + "get", + "list", + "update" + ], + "secrets": [ + "all" + ] + }, + "tenantId": "" + }, + { + "objectId": "", + "permissions": { + "certificates": [ + "backup", + "create", + "delete" + ], + "secrets": [ + "all" + ] + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableRbacAuthorization": { + "value": false + }, + "keys": { + "value": [ + { + "attributesExp": 1702648632, + "attributesNbf": 10000, + "name": "keyName", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "ipRules": [], + "virtualNetworkRules": [ + { + "action": "Allow", + "id": "" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "" + ] + }, + "service": "vault", + "subnetResourceId": "" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "secrets": { + "value": { + "secureList": [ + { + "attributesExp": 1702648632, + "attributesNbf": 10000, + "contentType": "Something", + "name": "secretName", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "value": "secretValue" + } + ] + } + }, + "softDeleteRetentionInDays": { + "value": 7 + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module vaults './Microsoft.KeyVault/vaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-kvvmin' + params: { + name: '<>kvvmin001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>kvvmin001" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.KeyVault/vaults/secrets/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.KeyVault/vaults/secrets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a7f6722 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/secrets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Key Vault Secrets User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6') + '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 secret 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(secret.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: secret +}] diff --git a/modules/Microsoft.KeyVault/vaults/secrets/deploy.bicep b/modules/Microsoft.KeyVault/vaults/secrets/deploy.bicep new file mode 100644 index 0000000..2abe71e --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/secrets/deploy.bicep @@ -0,0 +1,84 @@ +@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.') +param keyVaultName string + +@description('Required. The name of the secret.') +param name string + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Determines whether the object is enabled.') +param attributesEnabled bool = true + +@description('Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible.') +param attributesExp int = -1 + +@description('Optional. Not before date in seconds since 1970-01-01T00:00:00Z.') +param attributesNbf int = -1 + +@description('Optional. The content type of the secret.') +@secure() +param contentType string = '' + +@description('Required. The value of the secret. NOTE: "value" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets.') +@secure() +param value string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = { + name: keyVaultName +} + +resource secret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: name + parent: keyVault + tags: tags + properties: { + contentType: contentType + attributes: { + enabled: attributesEnabled + exp: attributesExp != -1 ? attributesExp : null + nbf: attributesNbf != -1 ? attributesNbf : null + } + value: value + } +} + +module secret_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: secret.id + } +}] + +@description('The name of the secret.') +output name string = secret.name + +@description('The resource ID of the secret.') +output resourceId string = secret.id + +@description('The name of the resource group the secret was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.KeyVault/vaults/secrets/readme.md b/modules/Microsoft.KeyVault/vaults/secrets/readme.md new file mode 100644 index 0000000..7316a5f --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/secrets/readme.md @@ -0,0 +1,154 @@ +# Key Vault Secret `[Microsoft.KeyVault/vaults/secrets]` + +This module deploys a key vault secret. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.KeyVault/vaults/secrets` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2019-09-01/vaults/secrets) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secret. | +| `value` | secureString | The value of the secret. NOTE: "value" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `keyVaultName` | string | The name of the parent key vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `attributesEnabled` | bool | `True` | Determines whether the object is enabled. | +| `attributesExp` | int | `-1` | Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible. | +| `attributesNbf` | int | `-1` | Not before date in seconds since 1970-01-01T00:00:00Z. | +| `contentType` | secureString | `''` | The content type of the secret. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `roleAssignments` | array | `[]` | 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}` | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secret. | +| `resourceGroupName` | string | The name of the resource group the secret was created in. | +| `resourceId` | string | The resource ID of the secret. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.KeyVault/vaults/secrets/version.json b/modules/Microsoft.KeyVault/vaults/secrets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/secrets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.KeyVault/vaults/version.json b/modules/Microsoft.KeyVault/vaults/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.KeyVault/vaults/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.KubernetesConfiguration/extensions/.test/min.parameters.json b/modules/Microsoft.KubernetesConfiguration/extensions/.test/min.parameters.json new file mode 100644 index 0000000..8beee2d --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/extensions/.test/min.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": "flux" + }, + "extensionType": { + "value": "microsoft.flux" + }, + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "releaseTrain": { + "value": "Stable" + }, + "releaseNamespace": { + "value": "flux-system" + } + } +} diff --git a/modules/Microsoft.KubernetesConfiguration/extensions/.test/parameters.json b/modules/Microsoft.KubernetesConfiguration/extensions/.test/parameters.json new file mode 100644 index 0000000..29ca850 --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/extensions/.test/parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "flux" + }, + "extensionType": { + "value": "microsoft.flux" + }, + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "releaseTrain": { + "value": "Stable" + }, + "releaseNamespace": { + "value": "flux-system" + }, + "version": { + "value": "0.5.2" + }, + "configurationSettings": { + "value": { + // "helm-controller.enabled": "false", + "source-controller.enabled": "true", + "kustomize-controller.enabled": "true", + "notification-controller.enabled": "false", + "image-automation-controller.enabled": "false", + "image-reflector-controller.enabled": "false" + } + } + } +} diff --git a/modules/Microsoft.KubernetesConfiguration/extensions/deploy.bicep b/modules/Microsoft.KubernetesConfiguration/extensions/deploy.bicep new file mode 100644 index 0000000..bfb3f28 --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/extensions/deploy.bicep @@ -0,0 +1,78 @@ +@description('Required. The name of the Flux Configuration.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. The name of the AKS cluster that should be configured.') +param clusterName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Configuration settings that are sensitive, as name-value pairs for configuring this extension.') +param configurationProtectedSettings object = {} + +@description('Optional. Configuration settings, as name-value pairs for configuring this extension.') +param configurationSettings object = {} + +@description('Required. Type of the Extension, of which this resource is an instance of. It must be one of the Extension Types registered with Microsoft.KubernetesConfiguration by the Extension publisher.') +param extensionType string + +@description('Optional. ReleaseTrain this extension participates in for auto-upgrade (e.g. Stable, Preview, etc.) - only if autoUpgradeMinorVersion is "true".') +param releaseTrain string = 'Stable' + +@description('Optional. Namespace where the extension Release must be placed, for a Cluster scoped extension. If this namespace does not exist, it will be created.') +param releaseNamespace string = '' + +@description('Optional. Namespace where the extension will be created for an Namespace scoped extension. If this namespace does not exist, it will be created.') +param targetNamespace string = '' + +@description('Optional. Version of the extension for this extension, if it is "pinned" to a specific version.') +param version string = '' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedCluster 'Microsoft.ContainerService/managedClusters@2022-06-01' existing = { + name: clusterName +} + +resource extension 'Microsoft.KubernetesConfiguration/extensions@2022-03-01' = { + name: name + scope: managedCluster + properties: { + autoUpgradeMinorVersion: !empty(version) ? false : true + configurationProtectedSettings: !empty(configurationProtectedSettings) ? configurationProtectedSettings : {} + configurationSettings: !empty(configurationSettings) ? configurationSettings : {} + extensionType: extensionType + releaseTrain: !empty(releaseTrain) ? releaseTrain : null + scope: { + cluster: !empty(releaseNamespace) ? { + releaseNamespace: releaseNamespace + } : null + namespace: !empty(targetNamespace) ? { + targetNamespace: targetNamespace + } : null + } + version: !empty(version) ? version : 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 deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.KubernetesConfiguration/extensions/readme.md b/modules/Microsoft.KubernetesConfiguration/extensions/readme.md new file mode 100644 index 0000000..46c3302 --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/extensions/readme.md @@ -0,0 +1,210 @@ +# Kubernetes Configuration Extensions `[Microsoft.KubernetesConfiguration/extensions]` + +This module deploys Kubernetes Configuration Extensions. + +## Navigation + +- [Prerequisites](#Prerequisites) +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Prerequisites + +Registration of your subscription with the AKS-ExtensionManager feature flag. Use the following command: + +```powershell +az feature register --namespace Microsoft.ContainerService --name AKS-ExtensionManager +``` + +Registration of the following Azure service providers. (It's OK to re-register an existing provider.) + +```powershell +az provider register --namespace Microsoft.Kubernetes +az provider register --namespace Microsoft.ContainerService +az provider register --namespace Microsoft.KubernetesConfiguration +``` + +For Details see [Prerequisites](https://docs.microsoft.com/en-us/azure/azure-arc/kubernetes/tutorial-use-gitops-flux2) +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.KubernetesConfiguration/extensions` | [2022-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KubernetesConfiguration/2022-03-01/extensions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `clusterName` | string | The name of the AKS cluster that should be configured. | +| `extensionType` | string | Type of the Extension, of which this resource is an instance of. It must be one of the Extension Types registered with Microsoft.KubernetesConfiguration by the Extension publisher. | +| `name` | string | The name of the Flux Configuration. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `configurationProtectedSettings` | object | `{object}` | Configuration settings that are sensitive, as name-value pairs for configuring this extension. | +| `configurationSettings` | object | `{object}` | Configuration settings, as name-value pairs for configuring this extension. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `releaseNamespace` | string | `''` | Namespace where the extension Release must be placed, for a Cluster scoped extension. If this namespace does not exist, it will be created. | +| `releaseTrain` | string | `'Stable'` | ReleaseTrain this extension participates in for auto-upgrade (e.g. Stable, Preview, etc.) - only if autoUpgradeMinorVersion is "true". | +| `targetNamespace` | string | `''` | Namespace where the extension will be created for an Namespace scoped extension. If this namespace does not exist, it will be created. | +| `version` | string | `''` | Version of the extension for this extension, if it is "pinned" to a specific version. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the extension. | +| `resourceGroupName` | string | The name of the resource group the extension was deployed into. | +| `resourceId` | string | The resource ID of the extension. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module extensions './Microsoft.KubernetesConfiguration/extensions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Extensions' + params: { + // Required parameters + clusterName: '<>-az-aks-kubenet-001' + extensionType: 'microsoft.flux' + name: 'flux' + // Non-required parameters + releaseNamespace: 'flux-system' + releaseTrain: 'Stable' + } +} +``` + +
+

+ +

+ +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 + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "extensionType": { + "value": "microsoft.flux" + }, + "name": { + "value": "flux" + }, + // Non-required parameters + "releaseNamespace": { + "value": "flux-system" + }, + "releaseTrain": { + "value": "Stable" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module extensions './Microsoft.KubernetesConfiguration/extensions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Extensions' + params: { + // Required parameters + clusterName: '<>-az-aks-kubenet-001' + extensionType: 'microsoft.flux' + name: 'flux' + // Non-required parameters + configurationSettings: { + 'image-automation-controller.enabled': 'false' + 'image-reflector-controller.enabled': 'false' + 'kustomize-controller.enabled': 'true' + 'notification-controller.enabled': 'false' + 'source-controller.enabled': 'true' + } + releaseNamespace: 'flux-system' + releaseTrain: 'Stable' + version: '0.5.2' + } +} +``` + +
+

+ +

+ +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 + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "extensionType": { + "value": "microsoft.flux" + }, + "name": { + "value": "flux" + }, + // Non-required parameters + "configurationSettings": { + "value": { + "image-automation-controller.enabled": "false", + "image-reflector-controller.enabled": "false", + "kustomize-controller.enabled": "true", + "notification-controller.enabled": "false", + "source-controller.enabled": "true" + } + }, + "releaseNamespace": { + "value": "flux-system" + }, + "releaseTrain": { + "value": "Stable" + }, + "version": { + "value": "0.5.2" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.KubernetesConfiguration/extensions/version.json b/modules/Microsoft.KubernetesConfiguration/extensions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/extensions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/min.parameters.json b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/min.parameters.json new file mode 100644 index 0000000..201ac22 --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/min.parameters.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "flux2" + }, + "scope": { + "value": "cluster" + }, + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "namespace": { + "value": "flux-system" + }, + "sourceKind": { + "value": "GitRepository" + }, + "gitRepository": { + "value": { + "url": "https://github.com/mspnp/aks-baseline", + "timeoutInSeconds": 180, + "syncIntervalInSeconds": 300, + "repositoryRef": { + "branch": "main" + }, + "sshKnownHosts": "" + } + } + } +} diff --git a/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/parameters.json b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/parameters.json new file mode 100644 index 0000000..e6f563f --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/.test/parameters.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "flux2" + }, + "scope": { + "value": "cluster" + }, + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "namespace": { + "value": "flux-system" + }, + "sourceKind": { + "value": "GitRepository" + }, + "gitRepository": { + "value": { + "url": "https://github.com/mspnp/aks-baseline", + "timeoutInSeconds": 180, + "syncIntervalInSeconds": 300, + "repositoryRef": { + "branch": "main" + }, + "sshKnownHosts": "" + } + }, + "kustomizations": { + "value": { + "unified": { + "path": "./cluster-manifests", + "dependsOn": [], + "timeoutInSeconds": 300, + "syncIntervalInSeconds": 300, + "prune": true, + "force": false + } + } + } + } +} diff --git a/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/deploy.bicep b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/deploy.bicep new file mode 100644 index 0000000..b0ef9b2 --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/deploy.bicep @@ -0,0 +1,83 @@ +@description('Required. The name of the Flux Configuration.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. The name of the AKS cluster that should be configured.') +param clusterName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Parameters to reconcile to the GitRepository source kind type.') +param bucket object = {} + +@description('Optional. Key-value pairs of protected configuration settings for the configuration.') +param configurationProtectedSettings object = {} + +@description('Optional. Parameters to reconcile to the GitRepository source kind type.') +param gitRepository object = {} + +@description('Optional. Array of kustomizations used to reconcile the artifact pulled by the source type on the cluster.') +param kustomizations object = {} + +@description('Required. The namespace to which this configuration is installed to. Maximum of 253 lower case alphanumeric characters, hyphen and period only.') +param namespace string + +@allowed([ + 'cluster' + 'namespace' +]) +@description('Required. Scope at which the configuration will be installed.') +param scope string + +@allowed([ + 'Bucket' + 'GitRepository' +]) +@description('Required. Source Kind to pull the configuration data from.') +param sourceKind string + +@description('Optional. Whether this configuration should suspend its reconciliation of its kustomizations and sources.') +param suspend bool = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedCluster 'Microsoft.ContainerService/managedClusters@2022-06-01' existing = { + name: clusterName +} + +resource fluxConfiguration 'Microsoft.KubernetesConfiguration/fluxConfigurations@2022-03-01' = { + name: name + scope: managedCluster + properties: { + bucket: !empty(bucket) ? bucket : null + configurationProtectedSettings: !empty(configurationProtectedSettings) ? configurationProtectedSettings : {} + gitRepository: !empty(gitRepository) ? gitRepository : null + kustomizations: !empty(kustomizations) ? kustomizations : {} + namespace: namespace + scope: scope + sourceKind: sourceKind + suspend: suspend + } +} + +@description('The name of the flux configuration.') +output name string = fluxConfiguration.name + +@description('The resource ID of the flux configuration.') +output resourceId string = fluxConfiguration.id + +@description('The name of the resource group the flux configuration was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/readme.md b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/readme.md new file mode 100644 index 0000000..ad3b68f --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/readme.md @@ -0,0 +1,254 @@ +# Kubernetes Configuration Flux Configurations `[Microsoft.KubernetesConfiguration/fluxConfigurations]` + +This module deploys Kubernetes Configuration Flux Configurations. + +## Navigation + +- [Prerequisites](#Prerequisites) +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Prerequisites + +Registration of your subscription with the AKS-ExtensionManager feature flag. Use the following command: + +```powershell +az feature register --namespace Microsoft.ContainerService --name AKS-ExtensionManager +``` + +Registration of the following Azure service providers. (It's OK to re-register an existing provider.) + +```powershell +az provider register --namespace Microsoft.Kubernetes +az provider register --namespace Microsoft.ContainerService +az provider register --namespace Microsoft.KubernetesConfiguration +``` + +For Details see [Prerequisites](https://docs.microsoft.com/en-us/azure/azure-arc/kubernetes/tutorial-use-gitops-flux2) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.KubernetesConfiguration/fluxConfigurations` | [2022-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KubernetesConfiguration/2022-03-01/fluxConfigurations) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `clusterName` | string | | The name of the AKS cluster that should be configured. | +| `name` | string | | The name of the Flux Configuration. | +| `namespace` | string | | The namespace to which this configuration is installed to. Maximum of 253 lower case alphanumeric characters, hyphen and period only. | +| `scope` | string | `[cluster, namespace]` | Scope at which the configuration will be installed. | +| `sourceKind` | string | `[Bucket, GitRepository]` | Source Kind to pull the configuration data from. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `bucket` | object | `{object}` | Parameters to reconcile to the GitRepository source kind type. | +| `configurationProtectedSettings` | object | `{object}` | Key-value pairs of protected configuration settings for the configuration. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `gitRepository` | object | `{object}` | Parameters to reconcile to the GitRepository source kind type. | +| `kustomizations` | object | `{object}` | Array of kustomizations used to reconcile the artifact pulled by the source type on the cluster. | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `suspend` | bool | `False` | Whether this configuration should suspend its reconciliation of its kustomizations and sources. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the flux configuration. | +| `resourceGroupName` | string | The name of the resource group the flux configuration was deployed into. | +| `resourceId` | string | The resource ID of the flux configuration. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module fluxConfigurations './Microsoft.KubernetesConfiguration/fluxConfigurations/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FluxConfigurations' + params: { + // Required parameters + clusterName: '<>-az-aks-kubenet-001' + name: 'flux2' + namespace: 'flux-system' + scope: 'cluster' + sourceKind: 'GitRepository' + // Non-required parameters + gitRepository: { + repositoryRef: { + branch: 'main' + } + sshKnownHosts: '' + syncIntervalInSeconds: 300 + timeoutInSeconds: 180 + url: 'https://github.com/mspnp/aks-baseline' + } + } +} +``` + +
+

+ +

+ +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 + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "name": { + "value": "flux2" + }, + "namespace": { + "value": "flux-system" + }, + "scope": { + "value": "cluster" + }, + "sourceKind": { + "value": "GitRepository" + }, + // Non-required parameters + "gitRepository": { + "value": { + "repositoryRef": { + "branch": "main" + }, + "sshKnownHosts": "", + "syncIntervalInSeconds": 300, + "timeoutInSeconds": 180, + "url": "https://github.com/mspnp/aks-baseline" + } + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module fluxConfigurations './Microsoft.KubernetesConfiguration/fluxConfigurations/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FluxConfigurations' + params: { + // Required parameters + clusterName: '<>-az-aks-kubenet-001' + name: 'flux2' + namespace: 'flux-system' + scope: 'cluster' + sourceKind: 'GitRepository' + // Non-required parameters + gitRepository: { + repositoryRef: { + branch: 'main' + } + sshKnownHosts: '' + syncIntervalInSeconds: 300 + timeoutInSeconds: 180 + url: 'https://github.com/mspnp/aks-baseline' + } + kustomizations: { + unified: { + dependsOn: [] + force: false + path: './cluster-manifests' + prune: true + syncIntervalInSeconds: 300 + timeoutInSeconds: 300 + } + } + } +} +``` + +
+

+ +

+ +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 + "clusterName": { + "value": "<>-az-aks-kubenet-001" + }, + "name": { + "value": "flux2" + }, + "namespace": { + "value": "flux-system" + }, + "scope": { + "value": "cluster" + }, + "sourceKind": { + "value": "GitRepository" + }, + // Non-required parameters + "gitRepository": { + "value": { + "repositoryRef": { + "branch": "main" + }, + "sshKnownHosts": "", + "syncIntervalInSeconds": 300, + "timeoutInSeconds": 180, + "url": "https://github.com/mspnp/aks-baseline" + } + }, + "kustomizations": { + "value": { + "unified": { + "dependsOn": [], + "force": false, + "path": "./cluster-manifests", + "prune": true, + "syncIntervalInSeconds": 300, + "timeoutInSeconds": 300 + } + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/version.json b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.KubernetesConfiguration/fluxConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Logic/workflows/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Logic/workflows/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..0ffc462 --- /dev/null +++ b/modules/Microsoft.Logic/workflows/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Sentinel Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4c81013-99ee-4d62-a7ee-b3f1f648599a') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + '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 logicApp 'Microsoft.Logic/workflows@2019-05-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(logicApp.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: logicApp +}] diff --git a/modules/Microsoft.Logic/workflows/.test/parameters.json b/modules/Microsoft.Logic/workflows/.test/parameters.json new file mode 100644 index 0000000..6436e5c --- /dev/null +++ b/modules/Microsoft.Logic/workflows/.test/parameters.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-lga-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "tags": { + "value": {} + }, + "workflowActions": { + "value": { + "HTTP": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://testStringForValidation.com", + "body": { + "HostPoolName": "[HostPoolName]", + "LAWorkspaceName": "[LAWorkspaceName]", + "LimitSecondsToForceLogOffUser": "[LimitSecondsToForceLogOffUser]", + "EndPeakTime": "[EndPeakTime]", + "BeginPeakTime": "[BeginPeakTime]", + "UtcOffset": "[UtcOffset]", + "LogOffMessageBody": "[LogOffMessageBody]", + "LogOffMessageTitle": "[LogOffMessageTitle]", + "MinimumNumberOfRDSH": 1, + "SessionThresholdPerCPU": 1, + "ResourceGroupName": "[ResourceGroupName]" + } + } + } + } + }, + "workflowTriggers": { + "value": { + "Recurrence": { + "recurrence": { + "frequency": "Minute", + "interval": 15 + }, + "type": "Recurrence" + } + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + } + } +} diff --git a/modules/Microsoft.Logic/workflows/deploy.bicep b/modules/Microsoft.Logic/workflows/deploy.bicep new file mode 100644 index 0000000..832ab70 --- /dev/null +++ b/modules/Microsoft.Logic/workflows/deploy.bicep @@ -0,0 +1,240 @@ +@description('Required. The logic app workflow name.') +param name string + +@description('Optional. The access control configuration for workflow actions.') +param actionsAccessControlConfiguration object = {} + +@description('Optional. The endpoints configuration: Access endpoint and outgoing IP addresses for the connector.') +param connectorEndpointsConfiguration object = {} + +@description('Optional. The access control configuration for accessing workflow run contents.') +param contentsAccessControlConfiguration object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Parameters for the definition template.') +param definitionParameters object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. The integration account.') +param integrationAccount object = {} + +@description('Optional. The integration service environment Id.') +param integrationServiceEnvironmentResourceId string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. The state. - NotSpecified, Completed, Enabled, Disabled, Deleted, Suspended.') +@allowed([ + 'NotSpecified' + 'Completed' + 'Enabled' + 'Disabled' + 'Deleted' + 'Suspended' +]) +param state string = 'Enabled' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The access control configuration for invoking workflow triggers.') +param triggersAccessControlConfiguration object = {} + +@description('Optional. The definitions for one or more actions to execute at workflow runtime.') +param workflowActions object = {} + +@description('Optional. The endpoints configuration: Access endpoint and outgoing IP addresses for the workflow.') +param workflowEndpointsConfiguration object = {} + +@description('Optional. The access control configuration for workflow management.') +param workflowManagementAccessControlConfiguration object = {} + +@description('Optional. The definitions for the outputs to return from a workflow run.') +param workflowOutputs object = {} + +@description('Optional. The definitions for one or more parameters that pass the values to use at your logic app\'s runtime.') +param workflowParameters object = {} + +@description('Optional. The definitions for one or more static results returned by actions as mock outputs when static results are enabled on those actions. In each action definition, the runtimeConfiguration.staticResult.name attribute references the corresponding definition inside staticResults.') +param workflowStaticResults object = {} + +@description('Optional. The definitions for one or more triggers that instantiate your workflow. You can define more than one trigger, but only with the Workflow Definition Language, not visually through the Logic Apps Designer.') +param workflowTriggers object = {} + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'WorkflowRuntime' +]) +param diagnosticLogCategoriesToEnable array = [ + 'WorkflowRuntime' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource logicApp 'Microsoft.Logic/workflows@2019-05-01' = { + name: name + location: location + tags: !empty(tags) ? tags : null + identity: identity + properties: { + state: state + endpointsConfiguration: { + workflow: workflowEndpointsConfiguration + connector: connectorEndpointsConfiguration + } + accessControl: { + triggers: !empty(triggersAccessControlConfiguration) ? triggersAccessControlConfiguration : null + contents: !empty(contentsAccessControlConfiguration) ? contentsAccessControlConfiguration : null + actions: !empty(actionsAccessControlConfiguration) ? actionsAccessControlConfiguration : null + workflowManagement: !empty(workflowManagementAccessControlConfiguration) ? workflowManagementAccessControlConfiguration : null + } + integrationAccount: !empty(integrationAccount) ? integrationAccount : null + integrationServiceEnvironment: !empty(integrationServiceEnvironmentResourceId) ? { + id: integrationServiceEnvironmentResourceId + } : null + + definition: { + '$schema': 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#' + actions: workflowActions + contentVersion: '1.0.0.0' + outputs: workflowOutputs + parameters: workflowParameters + staticResults: workflowStaticResults + triggers: workflowTriggers + } + parameters: definitionParameters + } +} + +resource logicApp_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${logicApp.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: logicApp +} + +resource logicApp_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: logicApp +} + +module logicApp_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LogicApp-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: logicApp.id + } +}] + +@description('The name of the logic app.') +output name string = logicApp.name + +@description('The resource group the logic app was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the logic app.') +output resourceId string = logicApp.id + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(logicApp.identity, 'principalId') ? logicApp.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = logicApp.location diff --git a/modules/Microsoft.Logic/workflows/readme.md b/modules/Microsoft.Logic/workflows/readme.md new file mode 100644 index 0000000..4ae0fff --- /dev/null +++ b/modules/Microsoft.Logic/workflows/readme.md @@ -0,0 +1,480 @@ +# Logic Apps `[Microsoft.Logic/workflows]` + +This module deploys a Logic App resource. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Logic/workflows` | [2019-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Logic/2019-05-01/workflows) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The logic app workflow name. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actionsAccessControlConfiguration` | object | `{object}` | | The access control configuration for workflow actions. | +| `connectorEndpointsConfiguration` | object | `{object}` | | The endpoints configuration: Access endpoint and outgoing IP addresses for the connector. | +| `contentsAccessControlConfiguration` | object | `{object}` | | The access control configuration for accessing workflow run contents. | +| `definitionParameters` | object | `{object}` | | Parameters for the definition template. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[WorkflowRuntime]` | `[WorkflowRuntime]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `integrationAccount` | object | `{object}` | | The integration account. | +| `integrationServiceEnvironmentResourceId` | string | `''` | | The integration service environment Id. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `state` | string | `'Enabled'` | `[Completed, Deleted, Disabled, Enabled, NotSpecified, Suspended]` | The state. - NotSpecified, Completed, Enabled, Disabled, Deleted, Suspended. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `triggersAccessControlConfiguration` | object | `{object}` | | The access control configuration for invoking workflow triggers. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `workflowActions` | object | `{object}` | | The definitions for one or more actions to execute at workflow runtime. | +| `workflowEndpointsConfiguration` | object | `{object}` | | The endpoints configuration: Access endpoint and outgoing IP addresses for the workflow. | +| `workflowManagementAccessControlConfiguration` | object | `{object}` | | The access control configuration for workflow management. | +| `workflowOutputs` | object | `{object}` | | The definitions for the outputs to return from a workflow run. | +| `workflowParameters` | object | `{object}` | | The definitions for one or more parameters that pass the values to use at your logic app's runtime. | +| `workflowStaticResults` | object | `{object}` | | The definitions for one or more static results returned by actions as mock outputs when static results are enabled on those actions. In each action definition, the runtimeConfiguration.staticResult.name attribute references the corresponding definition inside staticResults. | +| `workflowTriggers` | object | `{object}` | | The definitions for one or more triggers that instantiate your workflow. You can define more than one trigger, but only with the Workflow Definition Language, not visually through the Logic Apps Designer. | + + +### Parameter Usage `AccessControlConfiguration` + +- `actionsAccessControlConfiguration` +- `contentsAccessControlConfiguration` +- `triggersAccessControlConfiguration` +- `workflowManagementAccessControlConfiguration` + +

+ +Parameter JSON format + +```json +"AccessControlConfiguration": { + "value": { + "allowedCallerIpAddresses": [ + { + "addressRange": "string" + } + ], + "openAuthenticationPolicies": { + "policies": {} + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +'AccessControlConfiguration': { + allowedCallerIpAddresses: [ + { + addressRange: 'string' + } + ] + openAuthenticationPolicies: { + policies: {} + } +} +``` + +
+

+ +### Parameter Usage `EndpointsConfiguration` + +- `connectorEndpointsConfiguration` +- `workflowEndpointsConfiguration` + +

+ +Parameter JSON format + +```json +"EndpointsConfiguration": { + "value": { + "outgoingIpAddresses": [ + { + "address": "string" + } + ], + "accessEndpointIpAddresses": [ + { + "address": "string" + } + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +'EndpointsConfiguration': { + outgoingIpAddresses: [ + { + address: 'string' + } + ] + accessEndpointIpAddresses: [ + { + address: 'string' + } + ] +} +``` + +
+

+ +### Parameter Usage `workflow*` + +- To use the below parameters, see the following [documentation.](https://docs.microsoft.com/en-us/azure/logic-apps/logic-apps-workflow-definition-language) + - `workflowActions` + - `workflowOutputs` + - `workflowParameters` + - `workflowStaticResults` + - `workflowTriggers` + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the logic app. | +| `resourceGroupName` | string | The resource group the logic app was deployed into. | +| `resourceId` | string | The resource ID of the logic app. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module workflows './Microsoft.Logic/workflows/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workflows' + params: { + // Required parameters + name: '<>-az-lga-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + tags: {} + workflowActions: { + HTTP: { + inputs: { + body: { + BeginPeakTime: '[BeginPeakTime]' + EndPeakTime: '[EndPeakTime]' + HostPoolName: '[HostPoolName]' + LAWorkspaceName: '[LAWorkspaceName]' + LimitSecondsToForceLogOffUser: '[LimitSecondsToForceLogOffUser]' + LogOffMessageBody: '[LogOffMessageBody]' + LogOffMessageTitle: '[LogOffMessageTitle]' + MinimumNumberOfRDSH: 1 + ResourceGroupName: '[ResourceGroupName]' + SessionThresholdPerCPU: 1 + UtcOffset: '[UtcOffset]' + } + method: 'POST' + uri: 'https://testStringForValidation.com' + } + type: 'Http' + } + } + workflowTriggers: { + Recurrence: { + recurrence: { + frequency: 'Minute' + interval: 15 + } + type: 'Recurrence' + } + } + } +} +``` + +
+

+ +

+ +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": "<>-az-lga-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": {} + }, + "workflowActions": { + "value": { + "HTTP": { + "inputs": { + "body": { + "BeginPeakTime": "[BeginPeakTime]", + "EndPeakTime": "[EndPeakTime]", + "HostPoolName": "[HostPoolName]", + "LAWorkspaceName": "[LAWorkspaceName]", + "LimitSecondsToForceLogOffUser": "[LimitSecondsToForceLogOffUser]", + "LogOffMessageBody": "[LogOffMessageBody]", + "LogOffMessageTitle": "[LogOffMessageTitle]", + "MinimumNumberOfRDSH": 1, + "ResourceGroupName": "[ResourceGroupName]", + "SessionThresholdPerCPU": 1, + "UtcOffset": "[UtcOffset]" + }, + "method": "POST", + "uri": "https://testStringForValidation.com" + }, + "type": "Http" + } + } + }, + "workflowTriggers": { + "value": { + "Recurrence": { + "recurrence": { + "frequency": "Minute", + "interval": 15 + }, + "type": "Recurrence" + } + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Logic/workflows/version.json b/modules/Microsoft.Logic/workflows/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Logic/workflows/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.MachineLearningServices/workspaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.MachineLearningServices/workspaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..e4dd88e --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'AzureML Metrics Writer (preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '635dd51f-9968-44d3-b7fb-6d9a6bd613ae') + '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 workspace 'Microsoft.MachineLearningServices/workspaces@2021-04-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(workspace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: workspace +}] diff --git a/modules/Microsoft.MachineLearningServices/workspaces/.test/encr.parameters.json b/modules/Microsoft.MachineLearningServices/workspaces/.test/encr.parameters.json new file mode 100644 index 0000000..42ecb5e --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/.test/encr.parameters.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-mls-encr-001" + }, + "sku": { + "value": "Basic" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "systemAssignedIdentity": { + "value": false // Must be false if `primaryUserAssignedIdentity` is provided + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "primaryUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "amlworkspace", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.MachineLearningServices/workspaces/.test/min.parameters.json b/modules/Microsoft.MachineLearningServices/workspaces/.test/min.parameters.json new file mode 100644 index 0000000..012526c --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/.test/min.parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-mls-min-001" + }, + "sku": { + "value": "Basic" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "systemAssignedIdentity": { + "value": true + } + } +} diff --git a/modules/Microsoft.MachineLearningServices/workspaces/.test/parameters.json b/modules/Microsoft.MachineLearningServices/workspaces/.test/parameters.json new file mode 100644 index 0000000..ba9d9ed --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/.test/parameters.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-mls-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "Basic" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "systemAssignedIdentity": { + "value": false // Must be false if `primaryUserAssignedIdentity` is provided + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "description": { + "value": "The cake is a lie." + }, + "discoveryUrl": { + "value": "http://example.com" + }, + "imageBuildCompute": { + "value": "testcompute" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "primaryUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "computes": { + "value": [ + { + "name": "DefaultCPU", + "location": "westeurope", + "computeLocation": "westeurope", + "sku": "Basic", + "systemAssignedIdentity": false, + "userAssignedIdentities": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + }, + "description": "Default CPU Cluster", + "disableLocalAuth": false, + "computeType": "AmlCompute", + "properties": { + "enableNodePublicIp": true, + "isolatedNetwork": false, + "osType": "Linux", + "remoteLoginPortPublicAccess": "Disabled", + "scaleSettings": { + "maxNodeCount": 3, + "minNodeCount": 0, + "nodeIdleTimeBeforeScaleDown": "PT5M" + }, + "vmPriority": "Dedicated", + "vmSize": "STANDARD_DS11_V2" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "amlworkspace", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.MachineLearningServices/workspaces/computes/deploy.bicep b/modules/Microsoft.MachineLearningServices/workspaces/computes/deploy.bicep new file mode 100644 index 0000000..1c3be84 --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/computes/deploy.bicep @@ -0,0 +1,139 @@ +// ================ // +// Parameters // +// ================ // +@sys.description('Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment.') +param machineLearningWorkspaceName string + +@sys.description('Required. Name of the compute.') +@minLength(2) +@maxLength(16) +param name string + +@sys.description('Optional. Specifies the location of the resource.') +param location string = resourceGroup().location + +@sys.description('Optional. Specifies the sku, also referred as "edition". Required for creating a compute resource.') +@allowed([ + 'Basic' + 'Enterprise' + '' +]) +param sku string = '' + +@sys.description('Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID.') +param tags object = {} + +@sys.description('Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempontent, i.e. a second deployment will fail. Therefore, this flag needs to be set to "false" as long as the compute resource exists.') +param deployCompute bool = true + +@sys.description('Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID.') +param computeLocation string = resourceGroup().location + +@sys.description('Optional. The description of the Machine Learning compute.') +param description string = '' + +@sys.description('Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication.') +param disableLocalAuth bool = false + +@sys.description('Optional. ARM resource ID of the underlying compute.') +param resourceId string = '' + +@sys.description('Required. Set the object type.') +@allowed([ + 'AKS' + 'AmlCompute' + 'ComputeInstance' + 'Databricks' + 'DataFactory' + 'DataLakeAnalytics' + 'HDInsight' + 'Kubernetes' + 'SynapseSpark' + 'VirtualMachine' +]) +param computeType string + +@sys.description('Optional. The properties of the compute. Will be ignored in case "resourceId" is set.') +param properties object = {} + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +// Identity +@sys.description('Optional. Enables system assigned managed identity on the resource. Ignored when attaching a compute resource, i.e. when you provide a resource ID.') +param systemAssignedIdentity bool = false + +@sys.description('Optional. The ID(s) to assign to the resource. Ignored when attaching a compute resource, i.e. when you provide a resource ID.') +param userAssignedIdentities object = {} + +// ================// +// Variables // +// ================// +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : any(null) +} : any(null) + +// ============================= // +// Existing resources references // +// ============================= // +resource machineLearningWorkspace 'Microsoft.MachineLearningServices/workspaces@2021-04-01' existing = { + name: machineLearningWorkspaceName +} + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource machineLearningWorkspaceCompute 'Microsoft.MachineLearningServices/workspaces/computes@2022-01-01-preview' = if (deployCompute == true) { + name: name + location: location + tags: empty(resourceId) ? tags : any(null) + sku: empty(resourceId) ? { + name: sku + tier: sku + } : any(null) + parent: machineLearningWorkspace + identity: empty(resourceId) ? identity : any(null) + properties: union({ + description: description + disableLocalAuth: disableLocalAuth + computeType: computeType + }, (!empty(resourceId) ? { + resourceId: resourceId + } : { + computeLocation: computeLocation + properties: properties + })) +} + +// =========== // +// Outputs // +// =========== // +@sys.description('The name of the compute.') +output name string = machineLearningWorkspaceCompute.name + +@sys.description('The resource ID of the compute.') +output resourceId string = machineLearningWorkspaceCompute.id + +@sys.description('The resource group the compute was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The principal ID of the system assigned identity. Is null in case of attaching a compute resource, i.e. when you provide a resource ID.') +output systemAssignedPrincipalId string = empty(resourceId) ? (systemAssignedIdentity && contains(machineLearningWorkspaceCompute.identity, 'principalId') ? machineLearningWorkspaceCompute.identity.principalId : '') : '' + +@sys.description('The location the resource was deployed into.') +output location string = machineLearningWorkspaceCompute.location diff --git a/modules/Microsoft.MachineLearningServices/workspaces/computes/readme.md b/modules/Microsoft.MachineLearningServices/workspaces/computes/readme.md new file mode 100644 index 0000000..d6c19de --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/computes/readme.md @@ -0,0 +1,167 @@ +# Machine Learning Workspaces Computes `[Microsoft.MachineLearningServices/workspaces/computes]` + +This module deploys computes for an Machine Learning Workspace. +Attaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`). + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.MachineLearningServices/workspaces/computes` | [2022-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2022-01-01-preview/workspaces/computes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `computeType` | string | `[AKS, AmlCompute, ComputeInstance, Databricks, DataFactory, DataLakeAnalytics, HDInsight, Kubernetes, SynapseSpark, VirtualMachine]` | Set the object type. | +| `name` | string | | Name of the compute. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `machineLearningWorkspaceName` | string | The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `computeLocation` | string | `[resourceGroup().location]` | | Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID. | +| `deployCompute` | bool | `True` | | Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempontent, i.e. a second deployment will fail. Therefore, this flag needs to be set to "false" as long as the compute resource exists. | +| `description` | string | `''` | | The description of the Machine Learning compute. | +| `disableLocalAuth` | bool | `False` | | Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Specifies the location of the resource. | +| `properties` | object | `{object}` | | The properties of the compute. Will be ignored in case "resourceId" is set. | +| `resourceId` | string | `''` | | ARM resource ID of the underlying compute. | +| `sku` | string | `''` | `['', Basic, Enterprise]` | Specifies the sku, also referred as "edition". Required for creating a compute resource. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. Ignored when attaching a compute resource, i.e. when you provide a resource ID. | +| `tags` | object | `{object}` | | Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. Ignored when attaching a compute resource, i.e. when you provide a resource ID. | + + +### Parameter Usage: `properties` + +Properties for the compute resource to create. +Will be ignored in case a resource ID is provided. + +

+ +Parameter JSON format + +```json +"properties": { + "value": { + // See https://docs.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/computes?tabs=bicep#compute for the properties for the difference compute types + } +} +``` + +
+ +
+ +Bicep format + +```bicep +properties: { + // See https://docs.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/computes?tabs=bicep#compute for the properties for the difference compute types +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the compute. | +| `resourceGroupName` | string | The resource group the compute was deployed into. | +| `resourceId` | string | The resource ID of the compute. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. Is null in case of attaching a compute resource, i.e. when you provide a resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.MachineLearningServices/workspaces/computes/version.json b/modules/Microsoft.MachineLearningServices/workspaces/computes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/computes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.MachineLearningServices/workspaces/deploy.bicep b/modules/Microsoft.MachineLearningServices/workspaces/deploy.bicep new file mode 100644 index 0000000..7eeb19d --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/deploy.bicep @@ -0,0 +1,320 @@ +// ================ // +// Parameters // +// ================ // +@sys.description('Required. The name of the machine learning workspace.') +param name string + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Required. Specifies the SKU, also referred as \'edition\' of the Azure Machine Learning workspace.') +@allowed([ + 'Basic' + 'Enterprise' +]) +param sku string + +@sys.description('Required. The resource ID of the associated Storage Account.') +param associatedStorageAccountResourceId string + +@sys.description('Required. The resource ID of the associated Key Vault.') +param associatedKeyVaultResourceId string + +@sys.description('Required. The resource ID of the associated Application Insights.') +param associatedApplicationInsightsResourceId string + +@sys.description('Optional. The resource ID of the associated Container Registry.') +param associatedContainerRegistryResourceId string = '' + +@sys.allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@sys.description('Optional. Specify the type of lock.') +param lock string = '' + +@sys.description('Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service.') +param hbiWorkspace bool = false + +@sys.description('Optional. The flag to indicate whether to allow public access when behind VNet.') +param allowPublicAccessWhenBehindVnet bool = false + +@sys.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 = [] + +@sys.description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@sys.description('Optional. Computes to create respectively attach to the workspace.') +param computes array = [] + +@sys.description('Optional. Resource tags.') +param tags object = {} + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +// Identity +@sys.description('Conditional. Enables system assigned managed identity on the resource. Required if `userAssignedIdentities` is not provided.') +param systemAssignedIdentity bool = false + +@sys.description('Conditional. The ID(s) to assign to the resource. Required if `systemAssignedIdentity` is set to false.') +param userAssignedIdentities object = {} + +// Diagnostic Settings +@sys.description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@sys.description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@sys.description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@sys.description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@sys.description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@sys.description('Optional. The name of logs that will be streamed.') +@allowed([ + 'AmlComputeClusterEvent' + 'AmlComputeClusterNodeEvent' + 'AmlComputeJobEvent' + 'AmlComputeCpuGpuUtilization' + 'AmlRunStatusChangedEvent' +]) +param diagnosticLogCategoriesToEnable array = [ + 'AmlComputeClusterEvent' + 'AmlComputeClusterNodeEvent' + 'AmlComputeJobEvent' + 'AmlComputeCpuGpuUtilization' + 'AmlRunStatusChangedEvent' +] + +@sys.description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@sys.description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@sys.description('Optional. The description of this workspace.') +param description string = '' + +@sys.description('Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services.') +param discoveryUrl string = '' + +@sys.description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@sys.description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@sys.description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@sys.description('Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first.') +param cMKUserAssignedIdentityResourceId string = '' + +@sys.description('Optional. The compute name for image build.') +param imageBuildCompute string = '' + +@sys.description('Conditional. The user assigned identity resource ID that represents the workspace identity. Required if \'userAssignedIdentities\' is not empty and may not be used if \'systemAssignedIdentity\' is enabled.') +param primaryUserAssignedIdentity string = '' + +@sys.description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +// ================// +// Variables // +// ================// +var enableReferencedModulesTelemetry = false + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : any(null) +} : any(null) + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +// ================// +// Deployments // +// ================// +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource workspace 'Microsoft.MachineLearningServices/workspaces@2021-07-01' = { + name: name + location: location + tags: tags + sku: { + name: sku + tier: sku + } + identity: identity + properties: { + friendlyName: name + storageAccount: associatedStorageAccountResourceId + keyVault: associatedKeyVaultResourceId + applicationInsights: associatedApplicationInsightsResourceId + containerRegistry: !empty(associatedContainerRegistryResourceId) ? associatedContainerRegistryResourceId : null + hbiWorkspace: hbiWorkspace + allowPublicAccessWhenBehindVnet: allowPublicAccessWhenBehindVnet + description: description + discoveryUrl: discoveryUrl + encryption: !empty(cMKKeyName) ? { + status: 'Enabled' + identity: !empty(cMKUserAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } : null + keyVaultProperties: { + keyVaultArmId: cMKKeyVaultResourceId + keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVaultKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVaultKey.properties.keyUriWithVersion + } + } : null + imageBuildCompute: imageBuildCompute + primaryUserAssignedIdentity: primaryUserAssignedIdentity + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : 'Enabled') + } +} + +module workspace_computes 'computes/deploy.bicep' = [for compute in computes: { + name: '${workspace.name}-${compute.name}-compute' + params: { + machineLearningWorkspaceName: workspace.name + name: compute.name + location: compute.location + sku: contains(compute, 'sku') ? compute.sku : '' + systemAssignedIdentity: contains(compute, 'systemAssignedIdentity') ? compute.systemAssignedIdentity : false + userAssignedIdentities: contains(compute, 'userAssignedIdentities') ? compute.userAssignedIdentities : {} + tags: contains(compute, 'tags') ? compute.tags : {} + deployCompute: contains(compute, 'deployCompute') ? compute.deployCompute : true + computeLocation: contains(compute, 'computeLocation') ? compute.computeLocation : '' + description: contains(compute, 'description') ? compute.description : '' + disableLocalAuth: compute.disableLocalAuth + resourceId: contains(compute, 'resourceId') ? compute.resourceId : '' + computeType: compute.computeType + properties: contains(compute, 'properties') ? compute.properties : {} + } +}] + +resource workspace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${workspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: workspace +} + +resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: workspace +} + +module workspace_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-Workspace-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: workspace.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module workspace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-MLWorkspace-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: workspace.id + } +}] + +// ================// +// Outputs // +// ================// + +@sys.description('The resource ID of the machine learning service.') +output resourceId string = workspace.id + +@sys.description('The resource group the machine learning service was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The name of the machine learning service.') +output name string = workspace.name + +@sys.description('The principal ID of the system assigned identity.') +output principalId string = (!empty(identity) && contains(identity.type, 'SystemAssigned')) ? workspace.identity.principalId : '' + +@sys.description('The location the resource was deployed into.') +output location string = workspace.location diff --git a/modules/Microsoft.MachineLearningServices/workspaces/readme.md b/modules/Microsoft.MachineLearningServices/workspaces/readme.md new file mode 100644 index 0000000..d39335c --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/readme.md @@ -0,0 +1,800 @@ +# Machine Learning Workspaces `[Microsoft.MachineLearningServices/workspaces]` + +This module deploys a Machine Learning Services Workspace. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.MachineLearningServices/workspaces` | [2021-07-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2021-07-01/workspaces) | +| `Microsoft.MachineLearningServices/workspaces/computes` | [2022-01-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2022-01-01-preview/workspaces/computes) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `associatedApplicationInsightsResourceId` | string | | The resource ID of the associated Application Insights. | +| `associatedKeyVaultResourceId` | string | | The resource ID of the associated Key Vault. | +| `associatedStorageAccountResourceId` | string | | The resource ID of the associated Storage Account. | +| `name` | string | | The name of the machine learning workspace. | +| `sku` | string | `[Basic, Enterprise]` | Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `primaryUserAssignedIdentity` | string | `''` | The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled. | +| `systemAssignedIdentity` | bool | `False` | Enables system assigned managed identity on the resource. Required if `userAssignedIdentities` is not provided. | +| `userAssignedIdentities` | object | `{object}` | The ID(s) to assign to the resource. Required if `systemAssignedIdentity` is set to false. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowPublicAccessWhenBehindVnet` | bool | `False` | | The flag to indicate whether to allow public access when behind VNet. | +| `associatedContainerRegistryResourceId` | string | `''` | | The resource ID of the associated Container Registry. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `cMKUserAssignedIdentityResourceId` | string | `''` | | User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. | +| `computes` | _[computes](computes/readme.md)_ array | `[]` | | Computes to create respectively attach to the workspace. | +| `description` | string | `''` | | The description of this workspace. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AmlComputeClusterEvent, AmlComputeClusterNodeEvent, AmlComputeCpuGpuUtilization, AmlComputeJobEvent, AmlRunStatusChangedEvent]` | `[AmlComputeClusterEvent, AmlComputeClusterNodeEvent, AmlComputeCpuGpuUtilization, AmlComputeJobEvent, AmlRunStatusChangedEvent]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `discoveryUrl` | string | `''` | | URL for the discovery service to identify regional endpoints for machine learning experimentation services. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `hbiWorkspace` | bool | `False` | | The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service. | +| `imageBuildCompute` | string | `''` | | The compute name for image build. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | 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}` | | Resource tags. | + + +### Parameter Usage: `computes` + +Array to specify the compute resources to create respectively attach. +In case you provide a resource ID, it will attach the resource and ignore "properties". In this case "computeLocation", "sku", "systemAssignedIdentity", "userAssignedIdentities" as well as "tags" don't need to be provided respectively are being ignored. +Attaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML. I.e. for the first run set "deploy" to true, and after successful deployment to false. +For more information see https://docs.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/computes?tabs=bicep + +

+ +Parameter JSON format + +```json +"computes": { + "value": [ + // Attach existing resources + { + "name": "DefaultAKS", + "location": "westeurope", + "description": "Default AKS Cluster", + "disableLocalAuth": false, + "deployCompute": true, + "computeType": "AKS", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ContainerService/managedClusters/xxx" + }, + // Create new compute resource + { + "name": "DefaultCPU", + "location": "westeurope", + "computeLocation": "westeurope", + "sku": "Basic", + "systemAssignedIdentity": true, + "userAssignedIdentities": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + }, + "description": "Default CPU Cluster", + "disableLocalAuth": false, + "computeType": "AmlCompute", + "properties": { + "enableNodePublicIp": true, + "isolatedNetwork": false, + "osType": "Linux", + "remoteLoginPortPublicAccess": "Disabled", + "scaleSettings": { + "maxNodeCount": 3, + "minNodeCount": 0, + "nodeIdleTimeBeforeScaleDown": "PT5M" + }, + "vmPriority": "Dedicated", + "vmSize": "STANDARD_DS11_V2" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +computes: [ + // Attach existing resources + { + name: 'DefaultAKS' + location: 'westeurope' + description: 'Default AKS Cluster' + disableLocalAuth: false + deployCompute: true + computeType: 'AKS' + resourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ContainerService/managedClusters/xxx' + } + // Create new compute resource + { + name: 'DefaultCPU' + location: 'westeurope' + computeLocation: 'westeurope' + sku: 'Basic' + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + description: 'Default CPU Cluster' + disableLocalAuth: false + computeType: 'AmlCompute' + properties: { + enableNodePublicIp: true + isolatedNetwork: false + osType: 'Linux' + remoteLoginPortPublicAccess: 'Disabled' + scaleSettings: { + maxNodeCount: 3 + minNodeCount: 0 + nodeIdleTimeBeforeScaleDown: 'PT5M' + } + vmPriority: 'Dedicated' + vmSize: 'STANDARD_DS11_V2' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the machine learning service. | +| `principalId` | string | The principal ID of the system assigned identity. | +| `resourceGroupName` | string | The resource group the machine learning service was deployed into. | +| `resourceId` | string | The resource ID of the machine learning service. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.MachineLearningServices/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + associatedApplicationInsightsResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001' + associatedKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + associatedStorageAccountResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + name: '<>-az-mls-encr-001' + sku: 'Basic' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + primaryUserAssignedIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms' + ] + } + service: 'amlworkspace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + systemAssignedIdentity: false + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "name": { + "value": "<>-az-mls-encr-001" + }, + "sku": { + "value": "Basic" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "primaryUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms" + ] + }, + "service": "amlworkspace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "systemAssignedIdentity": { + "value": false + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.MachineLearningServices/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + associatedApplicationInsightsResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001' + associatedKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + associatedStorageAccountResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + name: '<>-az-mls-min-001' + sku: 'Basic' + // Non-required parameters + systemAssignedIdentity: true + } +} +``` + +
+

+ +

+ +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 + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "name": { + "value": "<>-az-mls-min-001" + }, + "sku": { + "value": "Basic" + }, + // Non-required parameters + "systemAssignedIdentity": { + "value": true + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.MachineLearningServices/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + associatedApplicationInsightsResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001' + associatedKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001' + associatedStorageAccountResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + name: '<>-az-mls-x-001' + sku: 'Basic' + // Non-required parameters + computes: [ + { + computeLocation: 'westeurope' + computeType: 'AmlCompute' + description: 'Default CPU Cluster' + disableLocalAuth: false + location: 'westeurope' + name: 'DefaultCPU' + properties: { + enableNodePublicIp: true + isolatedNetwork: false + osType: 'Linux' + remoteLoginPortPublicAccess: 'Disabled' + scaleSettings: { + maxNodeCount: 3 + minNodeCount: 0 + nodeIdleTimeBeforeScaleDown: 'PT5M' + } + vmPriority: 'Dedicated' + vmSize: 'STANDARD_DS11_V2' + } + sku: 'Basic' + systemAssignedIdentity: false + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } + ] + description: 'The cake is a lie.' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + discoveryUrl: 'http://example.com' + imageBuildCompute: 'testcompute' + lock: 'CanNotDelete' + primaryUserAssignedIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms' + ] + } + service: 'amlworkspace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + publicNetworkAccess: 'Enabled' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: false + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "associatedApplicationInsightsResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "associatedKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "associatedStorageAccountResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "name": { + "value": "<>-az-mls-x-001" + }, + "sku": { + "value": "Basic" + }, + // Non-required parameters + "computes": { + "value": [ + { + "computeLocation": "westeurope", + "computeType": "AmlCompute", + "description": "Default CPU Cluster", + "disableLocalAuth": false, + "location": "westeurope", + "name": "DefaultCPU", + "properties": { + "enableNodePublicIp": true, + "isolatedNetwork": false, + "osType": "Linux", + "remoteLoginPortPublicAccess": "Disabled", + "scaleSettings": { + "maxNodeCount": 3, + "minNodeCount": 0, + "nodeIdleTimeBeforeScaleDown": "PT5M" + }, + "vmPriority": "Dedicated", + "vmSize": "STANDARD_DS11_V2" + }, + "sku": "Basic", + "systemAssignedIdentity": false, + "userAssignedIdentities": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + ] + }, + "description": { + "value": "The cake is a lie." + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "discoveryUrl": { + "value": "http://example.com" + }, + "imageBuildCompute": { + "value": "testcompute" + }, + "lock": { + "value": "CanNotDelete" + }, + "primaryUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.azureml.ms" + ] + }, + "service": "amlworkspace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": false + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.MachineLearningServices/workspaces/version.json b/modules/Microsoft.MachineLearningServices/workspaces/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.MachineLearningServices/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..e2d2c4b --- /dev/null +++ b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + '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 userMsi 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(userMsi.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: userMsi +}] diff --git a/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.test/parameters.json b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.test/parameters.json new file mode 100644 index 0000000..d76c001 --- /dev/null +++ b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-msi-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep new file mode 100644 index 0000000..a655298 --- /dev/null +++ b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep @@ -0,0 +1,77 @@ +@description('Optional. Name of the User Assigned Identity.') +param name string = guid(resourceGroup().id) + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource userMsi 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: name + location: location + tags: tags +} + +resource userMsi_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${userMsi.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: userMsi +} + +module userMsi_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-UserMSI-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: userMsi.id + } +}] + +@description('The name of the user assigned identity.') +output name string = userMsi.name + +@description('The resource ID of the user assigned identity.') +output resourceId string = userMsi.id + +@description('The principal ID of the user assigned identity.') +output principalId string = userMsi.properties.principalId + +@description('The resource group the user assigned identity was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = userMsi.location diff --git a/modules/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md new file mode 100644 index 0000000..cd2c769 --- /dev/null +++ b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md @@ -0,0 +1,212 @@ +# User Assigned Identities `[Microsoft.ManagedIdentity/userAssignedIdentities]` + +This module deploys a user assigned identity. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2018-11-30](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2018-11-30/userAssignedIdentities) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `name` | string | `[guid(resourceGroup().id)]` | | Name of the User Assigned Identity. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the user assigned identity. | +| `principalId` | string | The principal ID of the user assigned identity. | +| `resourceGroupName` | string | The resource group the user assigned identity was deployed into. | +| `resourceId` | string | The resource ID of the user assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module userAssignedIdentities './Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-UserAssignedIdentities' + params: { + lock: 'CanNotDelete' + name: '<>-az-msi-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "lock": { + "value": "CanNotDelete" + }, + "name": { + "value": "<>-az-msi-x-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ManagedIdentity/userAssignedIdentities/version.json b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ManagedIdentity/userAssignedIdentities/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/.bicep/nested_registrationAssignment.bicep b/modules/Microsoft.ManagedServices/registrationDefinitions/.bicep/nested_registrationAssignment.bicep new file mode 100644 index 0000000..7bfca49 --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/.bicep/nested_registrationAssignment.bicep @@ -0,0 +1,15 @@ +param registrationDefinitionId string +param registrationAssignmentId string + +resource registrationAssignment 'Microsoft.ManagedServices/registrationAssignments@2019-09-01' = { + name: registrationAssignmentId + properties: { + registrationDefinitionId: registrationDefinitionId + } +} + +@description('The name of the registration assignment') +output name string = registrationAssignment.name + +@description('The resource ID of the registration assignment') +output resourceId string = registrationAssignment.id diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/.test/parameters.json b/modules/Microsoft.ManagedServices/registrationDefinitions/.test/parameters.json new file mode 100644 index 0000000..8fc6fc1 --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/.test/parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "Component Validation - Subscription assignment" + }, + "registrationDescription": { + "value": "Managed by Lighthouse" + }, + "managedByTenantId": { + "value": "195ee85d-2f10-4764-8352-a3c99aa772fb" + }, + "authorizations": { + "value": [ + { + "principalId": "e87a249c-b53b-4685-94fe-863af522e4ee", + "principalIdDisplayName": "ResourceModules-Reader", + "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + }, + { + "principalId": "e2f126a7-136e-443f-b39f-f73ddfd146b1", + "principalIdDisplayName": "ResourceModules-Contributor", + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "87813317-fb25-4c76-91fe-783af429d109", + "principalIdDisplayName": "ResourceModules-LHManagement", + "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46" + } + ] + } + } +} diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/.test/rg.parameters.json b/modules/Microsoft.ManagedServices/registrationDefinitions/.test/rg.parameters.json new file mode 100644 index 0000000..5e21414 --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/.test/rg.parameters.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "Component Validation - Resource group assignment" + }, + "registrationDescription": { + "value": "Managed by Lighthouse" + }, + "managedByTenantId": { + "value": "195ee85d-2f10-4764-8352-a3c99aa772fb" + }, + "resourceGroupName": { + "value": "validation-rg" + }, + "authorizations": { + "value": [ + { + "principalId": "e87a249c-b53b-4685-94fe-863af522e4ee", + "principalIdDisplayName": "ResourceModules-Reader", + "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + }, + { + "principalId": "e2f126a7-136e-443f-b39f-f73ddfd146b1", + "principalIdDisplayName": "ResourceModules-Contributor", + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "87813317-fb25-4c76-91fe-783af429d109", + "principalIdDisplayName": "ResourceModules-LHManagement", + "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46" + } + ] + } + } +} diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/deploy.bicep b/modules/Microsoft.ManagedServices/registrationDefinitions/deploy.bicep new file mode 100644 index 0000000..8dad5ff --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/deploy.bicep @@ -0,0 +1,75 @@ +targetScope = 'subscription' + +@description('Required. Specify a unique name for your offer/registration. i.e \' - - \'.') +param name string + +@description('Required. Description of the offer/registration. i.e. \'Managed by \'.') +param registrationDescription string + +@description('Required. Specify the tenant ID of the tenant which homes the principals you are delegating permissions to.') +param managedByTenantId string + +@description('Required. Specify an array of objects, containing object of Azure Active Directory principalId, a Azure roleDefinitionId, and an optional principalIdDisplayName. The roleDefinition specified is granted to the principalId in the provider\'s Active Directory and the principalIdDisplayName is visible to customers.') +param authorizations array + +@description('Optional. Specify the name of the Resource Group to delegate access to. If not provided, delegation will be done on the targeted subscription.') +param resourceGroupName string = '' + +@description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var registrationId = empty(resourceGroupName) ? guid(managedByTenantId, subscription().tenantId, subscription().subscriptionId) : guid(managedByTenantId, subscription().tenantId, subscription().subscriptionId, resourceGroupName) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource registrationDefinition 'Microsoft.ManagedServices/registrationDefinitions@2019-09-01' = { + name: registrationId + properties: { + registrationDefinitionName: name + description: registrationDescription + managedByTenantId: managedByTenantId + authorizations: authorizations + } +} + +resource registrationAssignment_sub 'Microsoft.ManagedServices/registrationAssignments@2019-09-01' = if (empty(resourceGroupName)) { + name: registrationId + properties: { + registrationDefinitionId: registrationDefinition.id + } +} + +module registrationAssignment_rg '.bicep/nested_registrationAssignment.bicep' = if (!empty(resourceGroupName)) { + name: '${uniqueString(deployment().name)}-RegDef-RegAssignment' + scope: resourceGroup(resourceGroupName) + params: { + registrationDefinitionId: registrationDefinition.id + registrationAssignmentId: registrationId + } +} + +@description('The name of the registration definition.') +output name string = registrationDefinition.name + +@description('The resource ID of the registration definition.') +output resourceId string = registrationDefinition.id + +@description('The subscription the registration definition was deployed into.') +output subscriptionName string = subscription().displayName + +@description('The registration assignment resource ID.') +output assignmentResourceId string = empty(resourceGroupName) ? registrationAssignment_sub.id : registrationAssignment_rg.outputs.resourceId diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/readme.md b/modules/Microsoft.ManagedServices/registrationDefinitions/readme.md new file mode 100644 index 0000000..93f7080 --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/readme.md @@ -0,0 +1,348 @@ +# Registration Definitions `[Microsoft.ManagedServices/registrationDefinitions]` + +This module deploys `registrationDefinitions` and `registrationAssignments` (often referred to as 'Lighthouse' or 'resource delegation') +on subscription or resource group scopes. This type of delegation is very similar to role assignments but here the principal that is +assigned a role is in a remote/managing Azure Active Directory tenant. The templates are run towards the tenant where +the Azure resources you want to delegate access to are, providing 'authorizations' (aka. access delegation) to principals in a +remote/managing tenant. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ManagedServices/registrationAssignments` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ManagedServices/2019-09-01/registrationAssignments) | +| `Microsoft.ManagedServices/registrationDefinitions` | [2019-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ManagedServices/2019-09-01/registrationDefinitions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `authorizations` | array | Specify an array of objects, containing object of Azure Active Directory principalId, a Azure roleDefinitionId, and an optional principalIdDisplayName. The roleDefinition specified is granted to the principalId in the provider's Active Directory and the principalIdDisplayName is visible to customers. | +| `managedByTenantId` | string | Specify the tenant ID of the tenant which homes the principals you are delegating permissions to. | +| `name` | string | Specify a unique name for your offer/registration. i.e ' - - '. | +| `registrationDescription` | string | Description of the offer/registration. i.e. 'Managed by '. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `resourceGroupName` | string | `''` | Specify the name of the Resource Group to delegate access to. If not provided, delegation will be done on the targeted subscription. | + + +### Parameter Usage: `authorizations` + +| Parameter Name | Type | Default Value | Possible values | Description | +| :----------------------- | :----- | :------------ | :-------------- | :------------------------------------------------------------------------------------------ | +| `principalId` | string | | GUID | Required. The object ID of the principal in the managing tenant to delegate permissions to. | +| `principalIdDisplayName` | string | `principalId` | | Optional. A display name of the principal that is delegated permissions to. | +| `roleDefinitionId` | string | | GUID | Required. The role definition ID to delegate to the principal in the managing tenant. | + +

+ +Parameter JSON format + +```json +"authorizations": { + "value": [ + // Delegates 'Reader' to a group in managing tenant (managedByTenantId) + { + "principalId": "9d949eef-00d5-45d9-8586-56be91a13398", + "principalIdDisplayName": "Reader-Group", + "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + }, + // Delegates 'Contributor' to a group in managing tenant (managedByTenantId) + { + "principalId": "06eb144f-1a10-4935-881b-757efd1d0b58", + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + // Delegates 'Managed Services Registration assignment Delete Role' to a group in managing tenant (managedByTenantId) + { + "principalId": "9cd792b0-dc7c-4551-84f8-dd87388030fb", + "principalIdDisplayName": "LighthouseManagement-Group", + "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +authorizations: [ + // Delegates 'Reader' to a group in managing tenant (managedByTenantId) + { + principalId: '9d949eef-00d5-45d9-8586-56be91a13398' + principalIdDisplayName: 'Reader-Group' + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + } + // Delegates 'Contributor' to a group in managing tenant (managedByTenantId) + { + principalId: '06eb144f-1a10-4935-881b-757efd1d0b58' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + // Delegates 'Managed Services Registration assignment Delete Role' to a group in managing tenant (managedByTenantId) + { + principalId: '9cd792b0-dc7c-4551-84f8-dd87388030fb' + principalIdDisplayName: 'LighthouseManagement-Group' + roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `assignmentResourceId` | string | The registration assignment resource ID. | +| `name` | string | The name of the registration definition. | +| `resourceId` | string | The resource ID of the registration definition. | +| `subscriptionName` | string | The subscription the registration definition was deployed into. | + +## Considerations + +This module can be deployed both at subscription and resource group level: + +- To deploy the module at resource group level, provide a valid name of an existing Resource Group in the `resourceGroupName` parameter. +- To deploy the module at the subscription level, leave the `resourceGroupName` parameter empty. + +### Permissions required to create delegations + +This deployment must be done by a non-guest account in the customer's tenant which has a role with the `Microsoft.Authorization/roleAssignments/write` permission, +such as [`Owner`](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner) for the subscription being onboarded (or which contains the resource groups that are being onboarded). + +If the subscription was created through the Cloud Solution Provider (CSP) program, any user who has the AdminAgent role in your service provider tenant can perform the deployment. + +**More info on this topic:** + + +### Permissions required to remove delegations + +#### From customer side + +Users in the customer's tenant who have a role with the `Microsoft.Authorization/roleAssignments/write` permission, such as +[`Owner`](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner) can remove service provider +access to that subscription (or to resource groups in that subscription). To do so, the user can go to the Service providers +page of the Azure portal and delete the delegation. + +#### From managing tenant side + +Users in a managing tenant can remove access to delegated resources if they were granted the +[`Managed Services Registration Assignment Delete Role`](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#managed-services-registration-assignment-delete-role) +for the customer's resources. If this role was not assigned to any service provider users, the delegation can **only** be +removed by a user in the customer's tenant. + +**More info on this topic:** + + +### Limitations with Lighthouse and resource delegation + +There are a couple of limitations that you should be aware of with Lighthouse: + +- Only allows resource delegation within Azure Resource Manager. Excludes Azure Active Directory, Microsoft 365 and Dynamics 365. +- Only supports delegation of control plane permissions. Excludes data plane access. +- Only supports subscription and resource group scopes. Excludes tenant and management group delegations. +- Only supports built-in roles, with the exception of `Owner`. Excludes the use of custom roles. + +**More info on this topic:** + + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module registrationDefinitions './Microsoft.ManagedServices/registrationDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RegistrationDefinitions' + params: { + // Required parameters + authorizations: [ + { + principalId: 'e87a249c-b53b-4685-94fe-863af522e4ee' + principalIdDisplayName: 'ResourceModules-Reader' + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + } + { + principalId: 'e2f126a7-136e-443f-b39f-f73ddfd146b1' + principalIdDisplayName: 'ResourceModules-Contributor' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '87813317-fb25-4c76-91fe-783af429d109' + principalIdDisplayName: 'ResourceModules-LHManagement' + roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' + } + ] + managedByTenantId: '195ee85d-2f10-4764-8352-a3c99aa772fb' + name: 'Component Validation - Subscription assignment' + registrationDescription: 'Managed by Lighthouse' + } +} +``` + +
+

+ +

+ +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 + "authorizations": { + "value": [ + { + "principalId": "e87a249c-b53b-4685-94fe-863af522e4ee", + "principalIdDisplayName": "ResourceModules-Reader", + "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + }, + { + "principalId": "e2f126a7-136e-443f-b39f-f73ddfd146b1", + "principalIdDisplayName": "ResourceModules-Contributor", + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "87813317-fb25-4c76-91fe-783af429d109", + "principalIdDisplayName": "ResourceModules-LHManagement", + "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46" + } + ] + }, + "managedByTenantId": { + "value": "195ee85d-2f10-4764-8352-a3c99aa772fb" + }, + "name": { + "value": "Component Validation - Subscription assignment" + }, + "registrationDescription": { + "value": "Managed by Lighthouse" + } + } +} +``` + +
+

+ +

Example 2: Rg

+ +
+ +via Bicep module + +```bicep +module registrationDefinitions './Microsoft.ManagedServices/registrationDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RegistrationDefinitions' + params: { + // Required parameters + authorizations: [ + { + principalId: 'e87a249c-b53b-4685-94fe-863af522e4ee' + principalIdDisplayName: 'ResourceModules-Reader' + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + } + { + principalId: 'e2f126a7-136e-443f-b39f-f73ddfd146b1' + principalIdDisplayName: 'ResourceModules-Contributor' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '87813317-fb25-4c76-91fe-783af429d109' + principalIdDisplayName: 'ResourceModules-LHManagement' + roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' + } + ] + managedByTenantId: '195ee85d-2f10-4764-8352-a3c99aa772fb' + name: 'Component Validation - Resource group assignment' + registrationDescription: 'Managed by Lighthouse' + // Non-required parameters + resourceGroupName: 'validation-rg' + } +} +``` + +
+

+ +

+ +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 + "authorizations": { + "value": [ + { + "principalId": "e87a249c-b53b-4685-94fe-863af522e4ee", + "principalIdDisplayName": "ResourceModules-Reader", + "roleDefinitionId": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + }, + { + "principalId": "e2f126a7-136e-443f-b39f-f73ddfd146b1", + "principalIdDisplayName": "ResourceModules-Contributor", + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "87813317-fb25-4c76-91fe-783af429d109", + "principalIdDisplayName": "ResourceModules-LHManagement", + "roleDefinitionId": "91c1777a-f3dc-4fae-b103-61d183457e46" + } + ] + }, + "managedByTenantId": { + "value": "195ee85d-2f10-4764-8352-a3c99aa772fb" + }, + "name": { + "value": "Component Validation - Resource group assignment" + }, + "registrationDescription": { + "value": "Managed by Lighthouse" + }, + // Non-required parameters + "resourceGroupName": { + "value": "validation-rg" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ManagedServices/registrationDefinitions/version.json b/modules/Microsoft.ManagedServices/registrationDefinitions/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ManagedServices/registrationDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Management/managementGroups/.test/parameters.json b/modules/Microsoft.Management/managementGroups/.test/parameters.json new file mode 100644 index 0000000..21e2524 --- /dev/null +++ b/modules/Microsoft.Management/managementGroups/.test/parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "testMG" + }, + "displayName": { + "value": "Test MG" + }, + "parentId": { + "value": "<>" + } + } +} diff --git a/modules/Microsoft.Management/managementGroups/deploy.bicep b/modules/Microsoft.Management/managementGroups/deploy.bicep new file mode 100644 index 0000000..932752c --- /dev/null +++ b/modules/Microsoft.Management/managementGroups/deploy.bicep @@ -0,0 +1,48 @@ +targetScope = 'managementGroup' + +@description('Required. The group ID of the Management group.') +param name string + +@description('Optional. The friendly name of the management group. If no value is passed then this field will be set to the group ID.') +param displayName string = '' + +@description('Optional. The management group parent ID. Defaults to current scope.') +param parentId string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managementGroup 'Microsoft.Management/managementGroups@2021-04-01' = { + name: name + scope: tenant() + properties: { + displayName: displayName + details: !empty(parentId) ? { + parent: { + id: '/providers/Microsoft.Management/managementGroups/${parentId}' + } + } : null + } +} + +@description('The name of the management group.') +output name string = managementGroup.name + +@description('The resource ID of the management group.') +output resourceId string = managementGroup.id diff --git a/modules/Microsoft.Management/managementGroups/readme.md b/modules/Microsoft.Management/managementGroups/readme.md new file mode 100644 index 0000000..258b1bc --- /dev/null +++ b/modules/Microsoft.Management/managementGroups/readme.md @@ -0,0 +1,183 @@ +# Management Groups `[Microsoft.Management/managementGroups]` + +This template will prepare the management group structure based on the provided parameter. + +This module has some known **limitations**: + +- It's not possible to change the display name of the root management group (the one that has the tenant GUID as ID) +- It can't manage the Root (/) management group + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Management/managementGroups` | [2021-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Management/2021-04-01/managementGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The group ID of the Management group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `displayName` | string | `''` | The friendly name of the management group. If no value is passed then this field will be set to the group ID. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `parentId` | string | `''` | The management group parent ID. Defaults to current scope. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the management group. | +| `resourceId` | string | The resource ID of the management group. | + +## Considerations + +This template is using a **Tenant level deployment**, meaning the user/principal deploying it needs to have the [proper access](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deploy-to-tenant#required-access) + +If owner access is excessive, the following rights roles will grant enough rights: + +- **Automation Job Operator** at **tenant** level (scope '/') +- **Management Group Contributor** at the top management group that needs to be managed + +Consider using the following script: + +```powershell +$PrincipalID = "" +$TopMGID = "" +New-AzRoleAssignment -ObjectId $PrincipalID -Scope "/" -RoleDefinitionName "Automation Job Operator" +New-AzRoleAssignment -ObjectId $PrincipalID -Scope "/providers/Microsoft.Management/managementGroups/$TopMGID" -RoleDefinitionName "Management Group Contributor" +``` + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module managementGroups './Microsoft.Management/managementGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ManagementGroups' + params: { + // Required parameters + name: 'testMG' + // Non-required parameters + displayName: 'Test MG' + parentId: '<>' + } +} +``` + +
+

+ +

+ +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": "testMG" + }, + // Non-required parameters + "displayName": { + "value": "Test MG" + }, + "parentId": { + "value": "<>" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Management/managementGroups/version.json b/modules/Microsoft.Management/managementGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Management/managementGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.NetApp/netAppAccounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..c92e122 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 netAppAccount 'Microsoft.NetApp/netAppAccounts@2022-01-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(netAppAccount.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: netAppAccount +}] diff --git a/modules/Microsoft.NetApp/netAppAccounts/.test/min.parameters.json b/modules/Microsoft.NetApp/netAppAccounts/.test/min.parameters.json new file mode 100644 index 0000000..029d5eb --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/.test/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": "<>-az-anf-min-001" + } + } +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/.test/nfs3.parameters.json b/modules/Microsoft.NetApp/netAppAccounts/.test/nfs3.parameters.json new file mode 100644 index 0000000..8718b5f --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/.test/nfs3.parameters.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-anf-nfs3-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "capacityPools": { + "value": [ + { + "name": "<>-az-anfcp-x-001", + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [ + { + "name": "anf3-vol01-nfsv3", + "usageThreshold": 107374182400, + "protocolTypes": [ + "NFSv3" + ], + "exportPolicyRules": [ + { + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true, + "nfsv3": true, + "nfsv41": false, + "allowedClients": "0.0.0.0/0" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "anf3-vol02-nfsv3", + "usageThreshold": 107374182400, + "protocolTypes": [ + "NFSv3" + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004" + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "<>-az-anfcp-x-002", + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } + } + } +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/.test/nfs41.parameters.json b/modules/Microsoft.NetApp/netAppAccounts/.test/nfs41.parameters.json new file mode 100644 index 0000000..2f961c1 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/.test/nfs41.parameters.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-anf-nfs41-001" + }, + "capacityPools": { + "value": [ + { + "name": "<>-az-anfcp-x-001", + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [ + { + "name": "anf4-vol01-nfsv41", + "usageThreshold": 107374182400, + "protocolTypes": [ + "NFSv4.1" + ], + "exportPolicyRules": [ + { + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true, + "nfsv3": false, + "nfsv41": true, + "allowedClients": "0.0.0.0/0" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "anf4-vol02-nfsv41", + "usageThreshold": 107374182400, + "protocolTypes": [ + "NFSv4.1" + ], + "exportPolicyRules": [ + { + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true, + "nfsv3": false, + "nfsv41": true, + "allowedClients": "0.0.0.0/0" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004" + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "<>-az-anfcp-x-002", + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } + } + } +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..e96f27d --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2022-01-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(capacityPool.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: capacityPool +}] diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/deploy.bicep b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/deploy.bicep new file mode 100644 index 0000000..c9a1232 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/deploy.bicep @@ -0,0 +1,117 @@ +@description('Conditional. The name of the parent NetApp account. Required if the template is used in a standalone deployment.') +param netAppAccountName string + +@description('Required. The name of the capacity pool.') +param name string + +@description('Optional. Location of the pool volume.') +param location string = resourceGroup().location + +@description('Optional. Tags for all resources.') +param tags object = {} + +@description('Optional. The pool service level.') +@allowed([ + 'Premium' + 'Standard' + 'StandardZRS' + 'Ultra' +]) +param serviceLevel string = 'Standard' + +@description('Required. Provisioned size of the pool (in bytes). Allowed values are in 4TiB chunks (value must be multiply of 4398046511104).') +param size int + +@description('Optional. The qos type of the pool.') +@allowed([ + 'Auto' + 'Manual' +]) +param qosType string = 'Auto' + +@description('Optional. List of volumnes to create in the capacity pool.') +param volumes array = [] + +@description('Optional. If enabled (true) the pool can contain cool Access enabled volumes.') +param coolAccess bool = false + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2022-01-01' existing = { + name: netAppAccountName +} + +resource capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2022-01-01' = { + name: name + parent: netAppAccount + location: location + tags: tags + properties: { + serviceLevel: serviceLevel + size: size + qosType: qosType + coolAccess: coolAccess + } +} + +@batchSize(1) +module capacityPool_volumes 'volumes/deploy.bicep' = [for (volume, index) in volumes: { + name: '${deployment().name}-Vol-${index}' + params: { + netAppAccountName: netAppAccount.name + capacityPoolName: capacityPool.name + name: volume.name + location: location + serviceLevel: serviceLevel + creationToken: contains(volume, 'creationToken') ? volume.creationToken : volume.name + usageThreshold: volume.usageThreshold + protocolTypes: contains(volume, 'protocolTypes') ? volume.protocolTypes : [] + subnetResourceId: volume.subnetResourceId + exportPolicyRules: contains(volume, 'exportPolicyRules') ? volume.exportPolicyRules : [] + roleAssignments: contains(volume, 'roleAssignments') ? volume.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module capacityPool_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: capacityPool.id + } +}] + +@description('The name of the Capacity Pool.') +output name string = capacityPool.name + +@description('The resource ID of the Capacity Pool.') +output resourceId string = capacityPool.id + +@description('The name of the Resource Group the Capacity Pool was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = capacityPool.location diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/readme.md b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/readme.md new file mode 100644 index 0000000..6bb72ca --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/readme.md @@ -0,0 +1,157 @@ +# Azure NetApp Files Capacity Pools `[Microsoft.NetApp/netAppAccounts/capacityPools]` + +This template deploys capacity pools in an Azure NetApp Files. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts/capacityPools/volumes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the capacity pool. | +| `size` | int | Provisioned size of the pool (in bytes). Allowed values are in 4TiB chunks (value must be multiply of 4398046511104). | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `netAppAccountName` | string | The name of the parent NetApp account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `coolAccess` | bool | `False` | | If enabled (true) the pool can contain cool Access enabled volumes. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location of the pool volume. | +| `qosType` | string | `'Auto'` | `[Auto, Manual]` | The qos type of the pool. | +| `roleAssignments` | array | `[]` | | 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'. | +| `serviceLevel` | string | `'Standard'` | `[Premium, Standard, StandardZRS, Ultra]` | The pool service level. | +| `tags` | object | `{object}` | | Tags for all resources. | +| `volumes` | _[volumes](volumes/readme.md)_ array | `[]` | | List of volumnes to create in the capacity pool. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Capacity Pool. | +| `resourceGroupName` | string | The name of the Resource Group the Capacity Pool was created in. | +| `resourceId` | string | The resource ID of the Capacity Pool. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/version.json b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d9a2a7e --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2022-01-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}/${split(resourceId, '/')[12]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(volume.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: volume +}] diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/deploy.bicep b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/deploy.bicep new file mode 100644 index 0000000..501b66a --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/deploy.bicep @@ -0,0 +1,102 @@ +@description('Conditional. The name of the parent NetApp account. Required if the template is used in a standalone deployment.') +param netAppAccountName string + +@description('Conditional. The name of the parent capacity pool. Required if the template is used in a standalone deployment.') +param capacityPoolName string + +@description('Required. The name of the pool volume.') +param name string + +@description('Optional. Location of the pool volume.') +param location string = resourceGroup().location + +@description('Optional. The pool service level. Must match the one of the parent capacity pool.') +@allowed([ + 'Premium' + 'Standard' + 'StandardZRS' + 'Ultra' +]) +param serviceLevel string = 'Standard' + +@description('Optional. A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription.') +param creationToken string = name + +@description('Required. Maximum storage quota allowed for a file system in bytes.') +param usageThreshold int + +@description('Optional. Set of protocol types.') +param protocolTypes array = [] + +@description('Required. The Azure Resource URI for a delegated subnet. Must have the delegation Microsoft.NetApp/volumes.') +param subnetResourceId string + +@description('Optional. Export policy rules.') +param exportPolicyRules array = [] + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2022-01-01' existing = { + name: netAppAccountName + + resource capacityPool 'capacityPools@2022-01-01' existing = { + name: capacityPoolName + } +} + +resource volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2022-01-01' = { + name: name + parent: netAppAccount::capacityPool + location: location + properties: { + serviceLevel: serviceLevel + creationToken: creationToken + usageThreshold: usageThreshold + protocolTypes: protocolTypes + subnetId: subnetResourceId + exportPolicy: !empty(exportPolicyRules) ? { + rules: exportPolicyRules + } : null + } +} + +module volume_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: volume.id + } +}] + +@description('The name of the Volume.') +output name string = volume.name + +@description('The Resource ID of the Volume.') +output resourceId string = volume.id + +@description('The name of the Resource Group the Volume was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = volume.location diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/readme.md b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/readme.md new file mode 100644 index 0000000..0b34653 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/readme.md @@ -0,0 +1,116 @@ +# Azure NetApp Files Capacity Pool Volumes `[Microsoft.NetApp/netAppAccounts/capacityPools/volumes]` + +This template deploys volumes in a capacity pool of an Azure NetApp files. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts/capacityPools/volumes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the pool volume. | +| `subnetResourceId` | string | The Azure Resource URI for a delegated subnet. Must have the delegation Microsoft.NetApp/volumes. | +| `usageThreshold` | int | Maximum storage quota allowed for a file system in bytes. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `capacityPoolName` | string | The name of the parent capacity pool. Required if the template is used in a standalone deployment. | +| `netAppAccountName` | string | The name of the parent NetApp account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `creationToken` | string | `[parameters('name')]` | | A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `exportPolicyRules` | array | `[]` | | Export policy rules. | +| `location` | string | `[resourceGroup().location]` | | Location of the pool volume. | +| `protocolTypes` | array | `[]` | | Set of protocol types. | +| `roleAssignments` | array | `[]` | | 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'. | +| `serviceLevel` | string | `'Standard'` | `[Premium, Standard, StandardZRS, Ultra]` | The pool service level. Must match the one of the parent capacity pool. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Volume. | +| `resourceGroupName` | string | The name of the Resource Group the Volume was created in. | +| `resourceId` | string | The Resource ID of the Volume. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/version.json b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/capacityPools/volumes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.NetApp/netAppAccounts/deploy.bicep b/modules/Microsoft.NetApp/netAppAccounts/deploy.bicep new file mode 100644 index 0000000..14e0462 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/deploy.bicep @@ -0,0 +1,128 @@ +@description('Required. The name of the NetApp account.') +param name string + +@description('Optional. Fully Qualified Active Directory DNS Domain Name (e.g. \'contoso.com\').') +param domainName string = '' + +@description('Optional. Required if domainName is specified. Username of Active Directory domain administrator, with permissions to create SMB server machine account in the AD domain.') +param domainJoinUser string = '' + +@description('Optional. Required if domainName is specified. Password of the user specified in domainJoinUser parameter.') +@secure() +param domainJoinPassword string = '' + +@description('Optional. Used only if domainName is specified. LDAP Path for the Organization Unit (OU) where SMB Server machine accounts will be created (i.e. \'OU=SecondLevel,OU=FirstLevel\').') +param domainJoinOU string = '' + +@description('Optional. Required if domainName is specified. Comma separated list of DNS server IP addresses (IPv4 only) required for the Active Directory (AD) domain join and SMB authentication operations to succeed.') +param dnsServers string = '' + +@description('Optional. Required if domainName is specified. NetBIOS name of the SMB server. A computer account with this prefix will be registered in the AD and used to mount volumes.') +param smbServerNamePrefix string = '' + +@description('Optional. Capacity pools to create.') +param capacityPools array = [] + +@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. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags for all resources.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +var activeDirectoryConnectionProperties = [ + { + username: !empty(domainName) ? domainJoinUser : null + password: !empty(domainName) ? domainJoinPassword : null + domain: !empty(domainName) ? domainName : null + dns: !empty(domainName) ? dnsServers : null + smbServerName: !empty(domainName) ? smbServerNamePrefix : null + organizationalUnit: !empty(domainJoinOU) ? domainJoinOU : null + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2022-01-01' = { + name: name + tags: tags + location: location + properties: { + activeDirectories: !empty(domainName) ? activeDirectoryConnectionProperties : null + } +} + +resource netAppAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${netAppAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: netAppAccount +} + +module netAppAccount_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ANFAccount-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: netAppAccount.id + } +}] + +module netAppAccount_capacityPools 'capacityPools/deploy.bicep' = [for (capacityPool, index) in capacityPools: { + name: '${uniqueString(deployment().name, location)}-ANFAccount-CapPool-${index}' + params: { + netAppAccountName: netAppAccount.name + name: capacityPool.name + location: location + size: capacityPool.size + serviceLevel: contains(capacityPool, 'serviceLevel') ? capacityPool.serviceLevel : 'Standard' + qosType: contains(capacityPool, 'qosType') ? capacityPool.qosType : 'Auto' + volumes: contains(capacityPool, 'volumes') ? capacityPool.volumes : [] + coolAccess: contains(capacityPool, 'coolAccess') ? capacityPool.coolAccess : false + roleAssignments: contains(capacityPool, 'roleAssignments') ? capacityPool.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the NetApp account.') +output name string = netAppAccount.name + +@description('The Resource ID of the NetApp account.') +output resourceId string = netAppAccount.id + +@description('The name of the Resource Group the NetApp account was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = netAppAccount.location diff --git a/modules/Microsoft.NetApp/netAppAccounts/readme.md b/modules/Microsoft.NetApp/netAppAccounts/readme.md new file mode 100644 index 0000000..72650e6 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/readme.md @@ -0,0 +1,644 @@ +# Azure NetApp Files `[Microsoft.NetApp/netAppAccounts]` + +This template deploys Azure NetApp Files. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.NetApp/netAppAccounts` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2022-01-01/netAppAccounts/capacityPools/volumes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NetApp account. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `capacityPools` | _[capacityPools](capacityPools/readme.md)_ array | `[]` | | Capacity pools to create. | +| `dnsServers` | string | `''` | | Required if domainName is specified. Comma separated list of DNS server IP addresses (IPv4 only) required for the Active Directory (AD) domain join and SMB authentication operations to succeed. | +| `domainJoinOU` | string | `''` | | Used only if domainName is specified. LDAP Path for the Organization Unit (OU) where SMB Server machine accounts will be created (i.e. 'OU=SecondLevel,OU=FirstLevel'). | +| `domainJoinPassword` | secureString | `''` | | Required if domainName is specified. Password of the user specified in domainJoinUser parameter. | +| `domainJoinUser` | string | `''` | | Required if domainName is specified. Username of Active Directory domain administrator, with permissions to create SMB server machine account in the AD domain. | +| `domainName` | string | `''` | | Fully Qualified Active Directory DNS Domain Name (e.g. 'contoso.com'). | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `smbServerNamePrefix` | string | `''` | | Required if domainName is specified. NetBIOS name of the SMB server. A computer account with this prefix will be registered in the AD and used to mount volumes. | +| `tags` | object | `{object}` | | Tags for all resources. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the NetApp account. | +| `resourceGroupName` | string | The name of the Resource Group the NetApp account was created in. | +| `resourceId` | string | The Resource ID of the NetApp account. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module netAppAccounts './Microsoft.NetApp/netAppAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetAppAccounts' + params: { + name: '<>-az-anf-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-anf-min-001" + } + } +} +``` + +
+

+ +

Example 2: Nfs3

+ +
+ +via Bicep module + +```bicep +module netAppAccounts './Microsoft.NetApp/netAppAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetAppAccounts' + params: { + // Required parameters + name: '<>-az-anf-nfs3-001' + // Non-required parameters + capacityPools: [ + { + name: '<>-az-anfcp-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + serviceLevel: 'Premium' + size: 4398046511104 + volumes: [ + { + exportPolicyRules: [ + { + allowedClients: '0.0.0.0/0' + nfsv3: true + nfsv41: false + ruleIndex: 1 + unixReadOnly: false + unixReadWrite: true + } + ] + name: 'anf3-vol01-nfsv3' + protocolTypes: [ + 'NFSv3' + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004' + usageThreshold: 107374182400 + } + { + name: 'anf3-vol02-nfsv3' + protocolTypes: [ + 'NFSv3' + ] + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004' + usageThreshold: 107374182400 + } + ] + } + { + name: '<>-az-anfcp-x-002' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + serviceLevel: 'Premium' + size: 4398046511104 + volumes: [] + } + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Contact: 'test.user@testcompany.com' + CostCenter: '7890' + Environment: 'Non-Prod' + PurchaseOrder: '1234' + Role: 'DeploymentValidation' + ServiceName: '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 + "name": { + "value": "<>-az-anf-nfs3-001" + }, + // Non-required parameters + "capacityPools": { + "value": [ + { + "name": "<>-az-anfcp-x-001", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [ + { + "exportPolicyRules": [ + { + "allowedClients": "0.0.0.0/0", + "nfsv3": true, + "nfsv41": false, + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true + } + ], + "name": "anf3-vol01-nfsv3", + "protocolTypes": [ + "NFSv3" + ], + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "usageThreshold": 107374182400 + }, + { + "name": "anf3-vol02-nfsv3", + "protocolTypes": [ + "NFSv3" + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "usageThreshold": 107374182400 + } + ] + }, + { + "name": "<>-az-anfcp-x-002", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [] + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Contact": "test.user@testcompany.com", + "CostCenter": "7890", + "Environment": "Non-Prod", + "PurchaseOrder": "1234", + "Role": "DeploymentValidation", + "ServiceName": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 3: Nfs41

+ +
+ +via Bicep module + +```bicep +module netAppAccounts './Microsoft.NetApp/netAppAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetAppAccounts' + params: { + // Required parameters + name: '<>-az-anf-nfs41-001' + // Non-required parameters + capacityPools: [ + { + name: '<>-az-anfcp-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + serviceLevel: 'Premium' + size: 4398046511104 + volumes: [ + { + exportPolicyRules: [ + { + allowedClients: '0.0.0.0/0' + nfsv3: false + nfsv41: true + ruleIndex: 1 + unixReadOnly: false + unixReadWrite: true + } + ] + name: 'anf4-vol01-nfsv41' + protocolTypes: [ + 'NFSv4.1' + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004' + usageThreshold: 107374182400 + } + { + exportPolicyRules: [ + { + allowedClients: '0.0.0.0/0' + nfsv3: false + nfsv41: true + ruleIndex: 1 + unixReadOnly: false + unixReadWrite: true + } + ] + name: 'anf4-vol02-nfsv41' + protocolTypes: [ + 'NFSv4.1' + ] + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004' + usageThreshold: 107374182400 + } + ] + } + { + name: '<>-az-anfcp-x-002' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + serviceLevel: 'Premium' + size: 4398046511104 + volumes: [] + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Contact: 'test.user@testcompany.com' + CostCenter: '7890' + Environment: 'Non-Prod' + PurchaseOrder: '1234' + Role: 'DeploymentValidation' + ServiceName: '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 + "name": { + "value": "<>-az-anf-nfs41-001" + }, + // Non-required parameters + "capacityPools": { + "value": [ + { + "name": "<>-az-anfcp-x-001", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [ + { + "exportPolicyRules": [ + { + "allowedClients": "0.0.0.0/0", + "nfsv3": false, + "nfsv41": true, + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true + } + ], + "name": "anf4-vol01-nfsv41", + "protocolTypes": [ + "NFSv4.1" + ], + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "usageThreshold": 107374182400 + }, + { + "exportPolicyRules": [ + { + "allowedClients": "0.0.0.0/0", + "nfsv3": false, + "nfsv41": true, + "ruleIndex": 1, + "unixReadOnly": false, + "unixReadWrite": true + } + ], + "name": "anf4-vol02-nfsv41", + "protocolTypes": [ + "NFSv4.1" + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-004", + "usageThreshold": 107374182400 + } + ] + }, + { + "name": "<>-az-anfcp-x-002", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "serviceLevel": "Premium", + "size": 4398046511104, + "volumes": [] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Contact": "test.user@testcompany.com", + "CostCenter": "7890", + "Environment": "Non-Prod", + "PurchaseOrder": "1234", + "Role": "DeploymentValidation", + "ServiceName": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.NetApp/netAppAccounts/version.json b/modules/Microsoft.NetApp/netAppAccounts/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.NetApp/netAppAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..2a8356a --- /dev/null +++ b/modules/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'ExpressRoute Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7896-14b4-4889-afef-fbb65a96e5a2') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource applicationGateway 'Microsoft.Network/applicationGateways@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(applicationGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: applicationGateway +}] diff --git a/modules/Microsoft.Network/applicationGateways/.test/parameters.json b/modules/Microsoft.Network/applicationGateways/.test/parameters.json new file mode 100644 index 0000000..7261318 --- /dev/null +++ b/modules/Microsoft.Network/applicationGateways/.test/parameters.json @@ -0,0 +1,371 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-apgw-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "enabled": true, + "firewallMode": "Detection", + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0", + "disabledRuleGroups": [], + "requestBodyCheck": true, + "maxRequestBodySizeInKb": 128, + "fileUploadLimitInMb": 100 + } + }, + "enableHttp2": { + "value": true + }, + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "port": 443, + "protocol": "Https", + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "port": 80, + "protocol": "Http", + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "requestTimeout": 30, + "probe": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/probes/privateVmHttpSettingProbe" + } + } + } + ] + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.8.6", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-apgw" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port443" + }, + "sslCertificate": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate" + }, + "protocol": "https", + "hostNames": [], + "requireServerNameIndication": false + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port4433" + }, + "sslCertificate": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate" + }, + "protocol": "https", + "hostNames": [], + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port80" + }, + "protocol": "Http", + "hostNames": [], + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port8080" + }, + "protocol": "Http", + "hostNames": [], + "requireServerNameIndication": false + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007" + } + } + } + ] + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "protocol": "Http", + "host": "10.0.0.4", + "path": "/", + "interval": 60, + "timeout": 15, + "unhealthyThreshold": 5, + "pickHostNameFromBackendHttpSettings": false, + "minServers": 3, + "match": { + "statusCodes": [ + "200", + "401" + ] + } + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "redirectType": "Permanent", + "targetListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443" + }, + "includePath": true, + "includeQueryString": true, + "requestRoutingRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect80-public443" + } + ] + } + }, + { + "name": "httpRedirect8080", + "properties": { + "redirectType": "Permanent", + "targetListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433" + }, + "includePath": true, + "includeQueryString": true, + "requestRoutingRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect8080-private4433" + } + ] + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "ruleType": "Basic", + "priority": 200, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443" + }, + "backendAddressPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/appServiceBackendPool" + }, + "backendHttpSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/appServiceBackendHttpsSetting" + } + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "ruleType": "Basic", + "priority": 250, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433" + }, + "backendAddressPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/privateVmBackendPool" + }, + "backendHttpSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/privateVmHttpSetting" + } + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "ruleType": "Basic", + "priority": 300, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect80" + }, + "redirectConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect80" + } + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "ruleType": "Basic", + "priority": 350, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect8080" + }, + "redirectConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect8080" + } + } + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "<>-az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "https://adp-<>-az-kv-x-001.vault.azure.net/secrets/applicationGatewaySslCertificate" + } + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/applicationGateways/deploy.bicep b/modules/Microsoft.Network/applicationGateways/deploy.bicep new file mode 100644 index 0000000..a7cc8d8 --- /dev/null +++ b/modules/Microsoft.Network/applicationGateways/deploy.bicep @@ -0,0 +1,368 @@ +@description('Required. Name of the Application Gateway.') +@maxLength(24) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Authentication certificates of the application gateway resource.') +param authenticationCertificates array = [] + +@description('Optional. Upper bound on number of Application Gateway capacity.') +param autoscaleMaxCapacity int = -1 + +@description('Optional. Lower bound on number of Application Gateway capacity.') +param autoscaleMinCapacity int = -1 + +@description('Optional. Backend address pool of the application gateway resource.') +param backendAddressPools array = [] + +@description('Optional. Backend http settings of the application gateway resource.') +param backendHttpSettingsCollection array = [] + +@description('Optional. Custom error configurations of the application gateway resource.') +param customErrorConfigurations array = [] + +@description('Optional. Whether FIPS is enabled on the application gateway resource.') +param enableFips bool = false + +@description('Optional. Whether HTTP2 is enabled on the application gateway resource.') +param enableHttp2 bool = false + +@description('Optional. The resource ID of an associated firewall policy. Should be configured for security reasons.') +param firewallPolicyId string = '' + +@description('Optional. Frontend IP addresses of the application gateway resource.') +param frontendIPConfigurations array = [] + +@description('Optional. Frontend ports of the application gateway resource.') +param frontendPorts array = [] + +@description('Optional. Subnets of the application gateway resource.') +param gatewayIPConfigurations array = [] + +@description('Optional. Enable request buffering.') +param enableRequestBuffering bool = false + +@description('Optional. Enable response buffering.') +param enableResponseBuffering bool = false + +@description('Optional. Http listeners of the application gateway resource.') +param httpListeners array = [] + +@description('Optional. Load distribution policies of the application gateway resource.') +param loadDistributionPolicies array = [] + +@description('Optional. PrivateLink configurations on application gateway.') +param privateLinkConfigurations array = [] + +@description('Optional. Probes of the application gateway resource.') +param probes array = [] + +@description('Optional. Redirect configurations of the application gateway resource.') +param redirectConfigurations array = [] + +@description('Optional. Request routing rules of the application gateway resource.') +param requestRoutingRules array = [] + +@description('Optional. Rewrite rules for the application gateway resource.') +param rewriteRuleSets array = [] + +@description('Optional. The name of the SKU for the Application Gateway.') +@allowed([ + 'Standard_Small' + 'Standard_Medium' + 'Standard_Large' + 'WAF_Medium' + 'WAF_Large' + 'Standard_v2' + 'WAF_v2' +]) +param sku string = 'WAF_Medium' + +@description('Optional. The number of Application instances to be configured.') +@minValue(1) +@maxValue(10) +param capacity int = 2 + +@description('Optional. SSL certificates of the application gateway resource.') +param sslCertificates array = [] + +@description('Optional. Ssl cipher suites to be enabled in the specified order to application gateway.') +@allowed([ + 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256' + 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_RSA_WITH_3DES_EDE_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_RSA_WITH_AES_256_CBC_SHA' + 'TLS_RSA_WITH_AES_256_CBC_SHA256' + 'TLS_RSA_WITH_AES_256_GCM_SHA384' +]) +param sslPolicyCipherSuites array = [ + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' +] + +@description('Optional. Ssl protocol enums.') +@allowed([ + 'TLSv1_0' + 'TLSv1_1' + 'TLSv1_2' +]) +param sslPolicyMinProtocolVersion string = 'TLSv1_2' + +@description('Optional. Ssl predefined policy name enums.') +@allowed([ + 'AppGwSslPolicy20150501' + 'AppGwSslPolicy20170401' + 'AppGwSslPolicy20170401S' + '' +]) +param sslPolicyName string = '' + +@description('Optional. Type of Ssl Policy.') +@allowed([ + 'Custom' + 'Predefined' +]) +param sslPolicyType string = 'Custom' + +@description('Optional. SSL profiles of the application gateway resource.') +param sslProfiles array = [] + +@description('Optional. Trusted client certificates of the application gateway resource.') +param trustedClientCertificates array = [] + +@description('Optional. Trusted Root certificates of the application gateway resource.') +param trustedRootCertificates array = [] + +@description('Optional. URL path map of the application gateway resource.') +param urlPathMaps array = [] + +@description('Optional. Application gateway web application firewall configuration. Should be configured for security reasons.') +param webApplicationFirewallConfiguration object = {} + +@description('Optional. A list of availability zones denoting where the resource needs to come from.') +param zones array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayPerformanceLog' + 'ApplicationGatewayFirewallLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayPerformanceLog' + 'ApplicationGatewayFirewallLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +var identityType = !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationGateway 'Microsoft.Network/applicationGateways@2021-08-01' = { + name: name + location: location + tags: tags + identity: identity + properties: union({ + authenticationCertificates: authenticationCertificates + autoscaleConfiguration: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 ? { + maxCapacity: autoscaleMaxCapacity + minCapacity: autoscaleMinCapacity + } : null + backendAddressPools: backendAddressPools + backendHttpSettingsCollection: backendHttpSettingsCollection + customErrorConfigurations: customErrorConfigurations + enableHttp2: enableHttp2 + firewallPolicy: !empty(firewallPolicyId) ? { + id: firewallPolicyId + } : null + forceFirewallPolicyAssociation: !empty(firewallPolicyId) + frontendIPConfigurations: frontendIPConfigurations + frontendPorts: frontendPorts + gatewayIPConfigurations: gatewayIPConfigurations + globalConfiguration: { + enableRequestBuffering: enableRequestBuffering + enableResponseBuffering: enableResponseBuffering + } + httpListeners: httpListeners + loadDistributionPolicies: loadDistributionPolicies + privateLinkConfigurations: privateLinkConfigurations + probes: probes + redirectConfigurations: redirectConfigurations + requestRoutingRules: requestRoutingRules + rewriteRuleSets: rewriteRuleSets + sku: { + name: sku + tier: endsWith(sku, 'v2') ? sku : substring(sku, 0, indexOf(sku, '_')) + capacity: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 ? null : capacity + } + sslCertificates: sslCertificates + sslPolicy: { + cipherSuites: sslPolicyCipherSuites + minProtocolVersion: sslPolicyMinProtocolVersion + policyName: empty(sslPolicyName) ? null : sslPolicyName + policyType: sslPolicyType + } + sslProfiles: sslProfiles + trustedClientCertificates: trustedClientCertificates + trustedRootCertificates: trustedRootCertificates + urlPathMaps: urlPathMaps + webApplicationFirewallConfiguration: webApplicationFirewallConfiguration + }, (enableFips ? { + enableFips: enableFips + } : {}), {}) + zones: zones +} + +resource applicationGateway_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${applicationGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: applicationGateway +} + +resource applicationGateway_diagnosticSettingName 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId + workspaceId: empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId + eventHubAuthorizationRuleId: empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId + eventHubName: empty(diagnosticEventHubName) ? null : diagnosticEventHubName + metrics: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsMetrics + logs: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsLogs + } + scope: applicationGateway +} + +module applicationGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: applicationGateway.id + } +}] + +@description('The name of the application gateway.') +output name string = applicationGateway.name + +@description('The resource ID of the application gateway.') +output resourceId string = applicationGateway.id + +@description('The resource group the application gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = applicationGateway.location diff --git a/modules/Microsoft.Network/applicationGateways/readme.md b/modules/Microsoft.Network/applicationGateways/readme.md new file mode 100644 index 0000000..bb3e588 --- /dev/null +++ b/modules/Microsoft.Network/applicationGateways/readme.md @@ -0,0 +1,956 @@ +# Network Application Gateways `[Microsoft.Network/applicationGateways]` + +This module deploys Network ApplicationGateways. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/applicationGateways` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/applicationGateways) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Gateway. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authenticationCertificates` | array | `[]` | | Authentication certificates of the application gateway resource. | +| `autoscaleMaxCapacity` | int | `-1` | | Upper bound on number of Application Gateway capacity. | +| `autoscaleMinCapacity` | int | `-1` | | Lower bound on number of Application Gateway capacity. | +| `backendAddressPools` | array | `[]` | | Backend address pool of the application gateway resource. | +| `backendHttpSettingsCollection` | array | `[]` | | Backend http settings of the application gateway resource. | +| `capacity` | int | `2` | | The number of Application instances to be configured. | +| `customErrorConfigurations` | array | `[]` | | Custom error configurations of the application gateway resource. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticLogCategoriesToEnable` | array | `[ApplicationGatewayAccessLog, ApplicationGatewayFirewallLog, ApplicationGatewayPerformanceLog]` | `[ApplicationGatewayAccessLog, ApplicationGatewayFirewallLog, ApplicationGatewayPerformanceLog]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableFips` | bool | `False` | | Whether FIPS is enabled on the application gateway resource. | +| `enableHttp2` | bool | `False` | | Whether HTTP2 is enabled on the application gateway resource. | +| `enableRequestBuffering` | bool | `False` | | Enable request buffering. | +| `enableResponseBuffering` | bool | `False` | | Enable response buffering. | +| `firewallPolicyId` | string | `''` | | The resource ID of an associated firewall policy. Should be configured for security reasons. | +| `frontendIPConfigurations` | array | `[]` | | Frontend IP addresses of the application gateway resource. | +| `frontendPorts` | array | `[]` | | Frontend ports of the application gateway resource. | +| `gatewayIPConfigurations` | array | `[]` | | Subnets of the application gateway resource. | +| `httpListeners` | array | `[]` | | Http listeners of the application gateway resource. | +| `loadDistributionPolicies` | array | `[]` | | Load distribution policies of the application gateway resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateLinkConfigurations` | array | `[]` | | PrivateLink configurations on application gateway. | +| `probes` | array | `[]` | | Probes of the application gateway resource. | +| `redirectConfigurations` | array | `[]` | | Redirect configurations of the application gateway resource. | +| `requestRoutingRules` | array | `[]` | | Request routing rules of the application gateway resource. | +| `rewriteRuleSets` | array | `[]` | | Rewrite rules for the application gateway resource. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'WAF_Medium'` | `[Standard_Large, Standard_Medium, Standard_Small, Standard_v2, WAF_Large, WAF_Medium, WAF_v2]` | The name of the SKU for the Application Gateway. | +| `sslCertificates` | array | `[]` | | SSL certificates of the application gateway resource. | +| `sslPolicyCipherSuites` | array | `[TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]` | `[TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384]` | Ssl cipher suites to be enabled in the specified order to application gateway. | +| `sslPolicyMinProtocolVersion` | string | `'TLSv1_2'` | `[TLSv1_0, TLSv1_1, TLSv1_2]` | Ssl protocol enums. | +| `sslPolicyName` | string | `''` | `['', AppGwSslPolicy20150501, AppGwSslPolicy20170401, AppGwSslPolicy20170401S]` | Ssl predefined policy name enums. | +| `sslPolicyType` | string | `'Custom'` | `[Custom, Predefined]` | Type of Ssl Policy. | +| `sslProfiles` | array | `[]` | | SSL profiles of the application gateway resource. | +| `tags` | object | `{object}` | | Resource tags. | +| `trustedClientCertificates` | array | `[]` | | Trusted client certificates of the application gateway resource. | +| `trustedRootCertificates` | array | `[]` | | Trusted Root certificates of the application gateway resource. | +| `urlPathMaps` | array | `[]` | | URL path map of the application gateway resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `webApplicationFirewallConfiguration` | object | `{object}` | | Application gateway web application firewall configuration. Should be configured for security reasons. | +| `zones` | array | `[]` | | A list of availability zones denoting where the resource needs to come from. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application gateway. | +| `resourceGroupName` | string | The resource group the application gateway was deployed into. | +| `resourceId` | string | The resource ID of the application gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module applicationGateways './Microsoft.Network/applicationGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ApplicationGateways' + params: { + // Required parameters + name: '<>-az-apgw-x-001' + // Non-required parameters + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/probes/privateVmHttpSettingProbe' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + enableHttp2: true + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.8.6' + privateIPAllocationMethod: 'Static' + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-apgw' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007' + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public' + } + frontendPort: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private' + } + frontendPort: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port4433' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public' + } + frontendPort: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port80' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private' + } + frontendPort: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port8080' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + lock: 'CanNotDelete' + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect80-public443' + } + ] + targetListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect8080-private4433' + } + ] + targetListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/appServiceBackendPool' + } + backendHttpSettings: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/appServiceBackendHttpsSetting' + } + httpListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/privateVmBackendPool' + } + backendHttpSettings: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/privateVmHttpSetting' + } + httpListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect80' + } + priority: 300 + redirectConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect80' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect8080' + } + priority: 350 + redirectConfiguration: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect8080' + } + ruleType: 'Basic' + } + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '<>-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: 'https://adp-<>-az-kv-x-001.vault.azure.net/secrets/applicationGatewaySslCertificate' + } + } + ] + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + webApplicationFirewallConfiguration: { + disabledRuleGroups: [] + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Detection' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-apgw-x-001" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "port": 443, + "protocol": "Https", + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "port": 80, + "probe": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/probes/privateVmHttpSettingProbe" + }, + "protocol": "Http", + "requestTimeout": 30 + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "enableHttp2": { + "value": true + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.8.6", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-apgw" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-007" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port443" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate" + } + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port4433" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/sslCertificates/<>-az-apgw-x-001-ssl-certificate" + } + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/public" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port80" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendIPConfigurations/private" + }, + "frontendPort": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/frontendPorts/port8080" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "host": "10.0.0.4", + "interval": 60, + "match": { + "statusCodes": [ + "200", + "401" + ] + }, + "minServers": 3, + "path": "/", + "pickHostNameFromBackendHttpSettings": false, + "protocol": "Http", + "timeout": 15, + "unhealthyThreshold": 5 + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect80-public443" + } + ], + "targetListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443" + } + } + }, + { + "name": "httpRedirect8080", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/requestRoutingRules/httpRedirect8080-private4433" + } + ], + "targetListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433" + } + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "backendAddressPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/appServiceBackendPool" + }, + "backendHttpSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/appServiceBackendHttpsSetting" + }, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/public443" + }, + "priority": 200, + "ruleType": "Basic" + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "backendAddressPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendAddressPools/privateVmBackendPool" + }, + "backendHttpSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/backendHttpSettingsCollection/privateVmHttpSetting" + }, + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/private4433" + }, + "priority": 250, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect80" + }, + "priority": 300, + "redirectConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect80" + }, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "httpListener": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/httpListeners/httpRedirect8080" + }, + "priority": 350, + "redirectConfiguration": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationGateways/<>-az-apgw-x-001/redirectConfigurations/httpRedirect8080" + }, + "ruleType": "Basic" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "<>-az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "https://adp-<>-az-kv-x-001.vault.azure.net/secrets/applicationGatewaySslCertificate" + } + } + ] + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "disabledRuleGroups": [], + "enabled": true, + "fileUploadLimitInMb": 100, + "firewallMode": "Detection", + "maxRequestBodySizeInKb": 128, + "requestBodyCheck": true, + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/applicationGateways/version.json b/modules/Microsoft.Network/applicationGateways/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/applicationGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d3032f7 --- /dev/null +++ b/modules/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'ExpressRoute Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7896-14b4-4889-afef-fbb65a96e5a2') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(applicationSecurityGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: applicationSecurityGroup +}] diff --git a/modules/Microsoft.Network/applicationSecurityGroups/.test/parameters.json b/modules/Microsoft.Network/applicationSecurityGroups/.test/parameters.json new file mode 100644 index 0000000..8bfef17 --- /dev/null +++ b/modules/Microsoft.Network/applicationSecurityGroups/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-asg-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/applicationSecurityGroups/deploy.bicep b/modules/Microsoft.Network/applicationSecurityGroups/deploy.bicep new file mode 100644 index 0000000..07d8c2b --- /dev/null +++ b/modules/Microsoft.Network/applicationSecurityGroups/deploy.bicep @@ -0,0 +1,75 @@ +@description('Required. Name of the Application Security Group.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2021-08-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource applicationSecurityGroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${applicationSecurityGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: applicationSecurityGroup +} + +module applicationSecurityGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppSecurityGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: applicationSecurityGroup.id + } +}] + +@description('The resource group the application security group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the application security group.') +output resourceId string = applicationSecurityGroup.id + +@description('The name of the application security group.') +output name string = applicationSecurityGroup.name + +@description('The location the resource was deployed into.') +output location string = applicationSecurityGroup.location diff --git a/modules/Microsoft.Network/applicationSecurityGroups/readme.md b/modules/Microsoft.Network/applicationSecurityGroups/readme.md new file mode 100644 index 0000000..66b053a --- /dev/null +++ b/modules/Microsoft.Network/applicationSecurityGroups/readme.md @@ -0,0 +1,219 @@ +# Application Security Groups `[Microsoft.Network/applicationSecurityGroups]` + +This module deploys an application security group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/applicationSecurityGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/applicationSecurityGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Security Group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application security group. | +| `resourceGroupName` | string | The resource group the application security group was deployed into. | +| `resourceId` | string | The resource ID of the application security group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module applicationSecurityGroups './Microsoft.Network/applicationSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ApplicationSecurityGroups' + params: { + // Required parameters + name: '<>-az-asg-x-001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-asg-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/applicationSecurityGroups/version.json b/modules/Microsoft.Network/applicationSecurityGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/applicationSecurityGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..84df6c1 --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'ExpressRoute Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7896-14b4-4889-afef-fbb65a96e5a2') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 azureFirewall 'Microsoft.Network/azureFirewalls@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(azureFirewall.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: azureFirewall +}] diff --git a/modules/Microsoft.Network/azureFirewalls/.test/addpip.parameters.json b/modules/Microsoft.Network/azureFirewalls/.test/addpip.parameters.json new file mode 100644 index 0000000..bd881f7 --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/.test/addpip.parameters.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fw-add-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-add-azfw" + }, + "additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-additional-fw" + } + ] + } + } +} diff --git a/modules/Microsoft.Network/azureFirewalls/.test/custompip.parameters.json b/modules/Microsoft.Network/azureFirewalls/.test/custompip.parameters.json new file mode 100644 index 0000000..68a1ce4 --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/.test/custompip.parameters.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fw-custompip-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-azfw" + }, + "publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } + } + } +} diff --git a/modules/Microsoft.Network/azureFirewalls/.test/min.parameters.json b/modules/Microsoft.Network/azureFirewalls/.test/min.parameters.json new file mode 100644 index 0000000..56f60cd --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fw-min-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-min-azfw" + } + } +} diff --git a/modules/Microsoft.Network/azureFirewalls/.test/parameters.json b/modules/Microsoft.Network/azureFirewalls/.test/parameters.json new file mode 100644 index 0000000..6f0a85e --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/.test/parameters.json @@ -0,0 +1,135 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fw-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-azfw" + }, + "azureFirewallSubnetPublicIpId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw" + }, + "applicationRuleCollections": { + "value": [ + { + "name": "allow-app-rules", + "properties": { + "priority": 100, + "action": { + "type": "allow" + }, + "rules": [ + { + "name": "allow-ase-tags", + "sourceAddresses": [ + "*" + ], + "protocols": [ + { + "protocolType": "HTTP", + "port": "80" + }, + { + "protocolType": "HTTPS", + "port": "443" + } + ], + "fqdnTags": [ + "AppServiceEnvironment", + "WindowsUpdate" + ] + }, + { + "name": "allow-ase-management", + "sourceAddresses": [ + "*" + ], + "protocols": [ + { + "protocolType": "HTTP", + "port": "80" + }, + { + "protocolType": "HTTPS", + "port": "443" + } + ], + "targetFqdns": [ + "management.azure.com" + ] + } + ] + } + } + ] + }, + "networkRuleCollections": { + "value": [ + { + "name": "allow-network-rules", + "properties": { + "priority": 100, + "action": { + "type": "allow" + }, + "rules": [ + { + "name": "allow-ntp", + "sourceAddresses": [ + "*" + ], + "destinationAddresses": [ + "*" + ], + "destinationPorts": [ + "123", + "12000" + ], + "protocols": [ + "Any" + ] + } + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/azureFirewalls/deploy.bicep b/modules/Microsoft.Network/azureFirewalls/deploy.bicep new file mode 100644 index 0000000..f40f4d5 --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/deploy.bicep @@ -0,0 +1,312 @@ +@description('Required. Name of the Azure Firewall.') +param name string + +@description('Optional. Name of an Azure Firewall SKU.') +@allowed([ + 'AZFW_VNet' + 'AZFW_Hub' +]) +param azureSkuName string = 'AZFW_VNet' + +@description('Optional. Tier of an Azure Firewall.') +@allowed([ + 'Standard' + 'Premium' +]) +param azureSkuTier string = 'Standard' + +@description('Required. Shared services Virtual Network resource ID. The virtual network ID containing AzureFirewallSubnet. If a public ip is not provided, then the public ip that is created as part of this module will be applied with the subnet provided in this variable.') +param vNetId string + +@description('Optional. The public ip resource ID to associate to the AzureFirewallSubnet. If empty, then the public ip that is created as part of this module will be applied to the AzureFirewallSubnet.') +param azureFirewallSubnetPublicIpId string = '' + +@description('Optional. This is to add any additional public ip configurations on top of the public ip with subnet ip configuration.') +param additionalPublicIpConfigurations array = [] + +@description('Optional. Specifies if a public ip should be created by default if one is not provided.') +param isCreateDefaultPublicIP bool = true + +@description('Optional. Specifies the properties of the public IP to create and be used by Azure Firewall. If it\'s not provided and publicIPAddressId is empty, a \'-pip\' suffix will be appended to the Firewall\'s name.') +param publicIPAddressObject object = {} + +@description('Optional. Collection of application rule collections used by Azure Firewall.') +param applicationRuleCollections array = [] + +@description('Optional. Collection of network rule collections used by Azure Firewall.') +param networkRuleCollections array = [] + +@description('Optional. Collection of NAT rule collections used by Azure Firewall.') +param natRuleCollections array = [] + +@description('Optional. Resource ID of the Firewall Policy that should be attached.') +param firewallPolicyId string = '' + +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +@description('Optional. The operation mode for Threat Intel.') +param threatIntelMode string = 'Deny' + +@description('Optional. Zone numbers e.g. 1,2,3.') +param zones array = [ + '1' + '2' + '3' +] + +@description('Optional. Diagnostic Storage Account resource identifier.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Log Analytics workspace resource identifier.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the Azure Firewall resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of firewall logs that will be streamed.') +@allowed([ + 'AzureFirewallApplicationRule' + 'AzureFirewallNetworkRule' + 'AzureFirewallDnsProxy' +]) +param diagnosticLogCategoriesToEnable array = [ + 'AzureFirewallApplicationRule' + 'AzureFirewallNetworkRule' + 'AzureFirewallDnsProxy' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var additionalPublicIpConfigurations_var = [for ipConfiguration in additionalPublicIpConfigurations: { + name: ipConfiguration.name + properties: { + publicIPAddress: contains(ipConfiguration, 'publicIPAddressResourceId') ? { + id: ipConfiguration.publicIPAddressResourceId + } : null + } +}] + +// ---------------------------------------------------------------------------- +// Prep ipConfigurations object AzureFirewallSubnet for different uses cases: +// 1. Use existing public ip +// 2. Use new public ip created in this module +// 3. Do not use a public ip if isCreateDefaultPublicIP is false + +var subnet_var = { + subnet: { + id: '${vNetId}/subnets/AzureFirewallSubnet' // The subnet name must be AzureFirewallSubnet + } +} +var existingPip = { + publicIPAddress: { + id: azureFirewallSubnetPublicIpId + } +} +var newPip = { + publicIPAddress: (empty(azureFirewallSubnetPublicIpId) && isCreateDefaultPublicIP) ? { + id: publicIPAddress.outputs.resourceId + } : null +} + +var ipConfigurations = concat([ + { + name: !empty(azureFirewallSubnetPublicIpId) ? last(split(azureFirewallSubnetPublicIpId, '/')) : publicIPAddress.outputs.name + //Use existing public ip, new public ip created in this module, or none if isCreateDefaultPublicIP is false + properties: union(subnet_var, !empty(azureFirewallSubnetPublicIpId) ? existingPip : {}, (isCreateDefaultPublicIP ? newPip : {})) + } + ], additionalPublicIpConfigurations_var) + +// ---------------------------------------------------------------------------- + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// create a public ip address if one is not provided and the flag is true +module publicIPAddress '../../Microsoft.Network/publicIPAddresses/deploy.bicep' = if (empty(azureFirewallSubnetPublicIpId) && isCreateDefaultPublicIP) { + name: '${uniqueString(deployment().name, location)}-Firewall-PIP' + params: { + name: contains(publicIPAddressObject, 'name') ? (!(empty(publicIPAddressObject.name)) ? publicIPAddressObject.name : '${name}-pip') : '${name}-pip' + publicIPPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId') ? (!(empty(publicIPAddressObject.publicIPPrefixResourceId)) ? publicIPAddressObject.publicIPPrefixResourceId : '') : '' + publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod') ? (!(empty(publicIPAddressObject.publicIPAllocationMethod)) ? publicIPAddressObject.publicIPAllocationMethod : 'Static') : 'Static' + skuName: contains(publicIPAddressObject, 'skuName') ? (!(empty(publicIPAddressObject.skuName)) ? publicIPAddressObject.skuName : 'Standard') : 'Standard' + skuTier: contains(publicIPAddressObject, 'skuTier') ? (!(empty(publicIPAddressObject.skuTier)) ? publicIPAddressObject.skuTier : 'Regional') : 'Regional' + roleAssignments: contains(publicIPAddressObject, 'roleAssignments') ? (!empty(publicIPAddressObject.roleAssignments) ? publicIPAddressObject.roleAssignments : []) : [] + diagnosticMetricsToEnable: contains(publicIPAddressObject, 'diagnosticMetricsToEnable') ? (!(empty(publicIPAddressObject.diagnosticMetricsToEnable)) ? publicIPAddressObject.diagnosticMetricsToEnable : [ + 'AllMetrics' + ]) : [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: contains(publicIPAddressObject, 'diagnosticLogCategoriesToEnable') ? (!(empty(publicIPAddressObject.diagnosticLogCategoriesToEnable)) ? publicIPAddressObject.diagnosticLogCategoriesToEnable : [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ]) : [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] + location: location + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + lock: lock + tags: tags + zones: zones + } +} + +resource azureFirewall 'Microsoft.Network/azureFirewalls@2021-08-01' = { + name: name + location: location + zones: length(zones) == 0 ? null : zones + tags: tags + properties: { + threatIntelMode: threatIntelMode + firewallPolicy: empty(firewallPolicyId) ? null : { + id: firewallPolicyId + } + ipConfigurations: ipConfigurations + sku: { + name: azureSkuName + tier: azureSkuTier + } + applicationRuleCollections: applicationRuleCollections + natRuleCollections: natRuleCollections + networkRuleCollections: networkRuleCollections + } + dependsOn: [ + publicIPAddress + ] +} + +resource azureFirewall_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${azureFirewall.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: azureFirewall +} + +resource azureFirewall_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: azureFirewall +} + +module azureFirewall_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AzFW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: azureFirewall.id + } +}] + +@description('The resource ID of the Azure firewall.') +output resourceId string = azureFirewall.id + +@description('The name of the Azure firewall.') +output name string = azureFirewall.name + +@description('The resource group the Azure firewall was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The private IP of the Azure firewall.') +output privateIp string = azureFirewall.properties.ipConfigurations[0].properties.privateIPAddress + +@description('The public ipconfiguration object for the AzureFirewallSubnet.') +output ipConfAzureFirewallSubnet object = azureFirewall.properties.ipConfigurations[0] + +@description('List of Application Rule Collections.') +output applicationRuleCollections array = applicationRuleCollections + +@description('List of Network Rule Collections.') +output networkRuleCollections array = networkRuleCollections + +@description('Collection of NAT rule collections used by Azure Firewall.') +output natRuleCollections array = natRuleCollections + +@description('The location the resource was deployed into.') +output location string = azureFirewall.location diff --git a/modules/Microsoft.Network/azureFirewalls/readme.md b/modules/Microsoft.Network/azureFirewalls/readme.md new file mode 100644 index 0000000..deb2c76 --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/readme.md @@ -0,0 +1,778 @@ +# Azure Firewalls `[Microsoft.Network/azureFirewalls]` + +This module deploys a firewall. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/azureFirewalls` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/azureFirewalls) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Firewall. | +| `vNetId` | string | Shared services Virtual Network resource ID. The virtual network ID containing AzureFirewallSubnet. If a public ip is not provided, then the public ip that is created as part of this module will be applied with the subnet provided in this variable. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalPublicIpConfigurations` | array | `[]` | | This is to add any additional public ip configurations on top of the public ip with subnet ip configuration. | +| `applicationRuleCollections` | array | `[]` | | Collection of application rule collections used by Azure Firewall. | +| `azureFirewallSubnetPublicIpId` | string | `''` | | The public ip resource ID to associate to the AzureFirewallSubnet. If empty, then the public ip that is created as part of this module will be applied to the AzureFirewallSubnet. | +| `azureSkuName` | string | `'AZFW_VNet'` | `[AZFW_Hub, AZFW_VNet]` | Name of an Azure Firewall SKU. | +| `azureSkuTier` | string | `'Standard'` | `[Premium, Standard]` | Tier of an Azure Firewall. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AzureFirewallApplicationRule, AzureFirewallDnsProxy, AzureFirewallNetworkRule]` | `[AzureFirewallApplicationRule, AzureFirewallDnsProxy, AzureFirewallNetworkRule]` | The name of firewall logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Diagnostic Storage Account resource identifier. | +| `diagnosticWorkspaceId` | string | `''` | | Log Analytics workspace resource identifier. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `firewallPolicyId` | string | `''` | | Resource ID of the Firewall Policy that should be attached. | +| `isCreateDefaultPublicIP` | bool | `True` | | Specifies if a public ip should be created by default if one is not provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natRuleCollections` | array | `[]` | | Collection of NAT rule collections used by Azure Firewall. | +| `networkRuleCollections` | array | `[]` | | Collection of network rule collections used by Azure Firewall. | +| `publicIPAddressObject` | object | `{object}` | | Specifies the properties of the public IP to create and be used by Azure Firewall. If it's not provided and publicIPAddressId is empty, a '-pip' suffix will be appended to the Firewall's name. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the Azure Firewall resource. | +| `threatIntelMode` | string | `'Deny'` | `[Alert, Deny, Off]` | The operation mode for Threat Intel. | +| `zones` | array | `[1, 2, 3]` | | Zone numbers e.g. 1,2,3. | + + +### Parameter Usage: `additionalPublicIpConfigurations` + +Create additional public ip configurations from existing public ips + +

+ +Parameter JSON format + +```json +"additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01" + }, + { + "name": "ipConfig02", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01' + } + { + name: 'ipConfig02' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02' + } +] +``` + +
+ + +### Parameter Usage: `publicIPAddressObject` + +The Public IP Address object to create as part of the module. This will be created if `isCreateDefaultPublicIP` is true (which it is by default). If not provided, the name and other configurations will be set by default. + + +
+ +Parameter JSON format + +```json +"publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } +} +``` + +
+ + + +
+ +Bicep format + + +```bicep +publicIPAddressObject: { + name: 'mypip' + publicIPPrefixResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPPrefixes/myprefix' + publicIPAllocationMethod: 'Dynamic' + skuName: 'Basic' + skuTier: 'Regional' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '<>' + ] + } + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] +} +``` + +
+ + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +
+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `applicationRuleCollections` | array | List of Application Rule Collections. | +| `ipConfAzureFirewallSubnet` | object | The public ipconfiguration object for the AzureFirewallSubnet. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Azure firewall. | +| `natRuleCollections` | array | Collection of NAT rule collections used by Azure Firewall. | +| `networkRuleCollections` | array | List of Network Rule Collections. | +| `privateIp` | string | The private IP of the Azure firewall. | +| `resourceGroupName` | string | The resource group the Azure firewall was deployed into. | +| `resourceId` | string | The resource ID of the Azure firewall. | + +## Considerations + +The `applicationRuleCollections` parameter accepts a JSON Array of AzureFirewallApplicationRule objects. +The `networkRuleCollections` parameter accepts a JSON Array of AzureFirewallNetworkRuleCollection objects. + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Addpip

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AzureFirewalls' + params: { + // Required parameters + name: '<>-az-fw-add-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-add-azfw' + // Non-required parameters + additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-additional-fw' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-fw-add-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-add-azfw" + }, + // Non-required parameters + "additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-additional-fw" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Custompip

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AzureFirewalls' + params: { + // Required parameters + name: '<>-az-fw-custompip-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-azfw' + // Non-required parameters + publicIPAddressObject: { + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: 'adp-<>-az-pip-custom-x-fw' + publicIPAllocationMethod: 'Static' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-fw-custompip-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-azfw" + }, + // Non-required parameters + "publicIPAddressObject": { + "value": { + "diagnosticLogCategoriesToEnable": [ + "DDoSMitigationFlowLogs", + "DDoSMitigationReports", + "DDoSProtectionNotifications" + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPAllocationMethod": "Static", + "publicIPPrefixResourceId": "", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "skuName": "Standard", + "skuTier": "Regional" + } + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AzureFirewalls' + params: { + // Required parameters + name: '<>-az-fw-min-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-min-azfw' + } +} +``` + +
+

+ +

+ +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": "<>-az-fw-min-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-min-azfw" + } + } +} +``` + +
+

+ +

Example 4: Parameters

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AzureFirewalls' + params: { + // Required parameters + name: '<>-az-fw-x-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-azfw' + // Non-required parameters + applicationRuleCollections: [ + { + name: 'allow-app-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + fqdnTags: [ + 'AppServiceEnvironment' + 'WindowsUpdate' + ] + name: 'allow-ase-tags' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + } + { + name: 'allow-ase-management' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + targetFqdns: [ + 'management.azure.com' + ] + } + ] + } + } + ] + azureFirewallSubnetPublicIpId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + networkRuleCollections: [ + { + name: 'allow-network-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationPorts: [ + '12000' + '123' + ] + name: 'allow-ntp' + protocols: [ + 'Any' + ] + sourceAddresses: [ + '*' + ] + } + ] + } + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-fw-x-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-azfw" + }, + // Non-required parameters + "applicationRuleCollections": { + "value": [ + { + "name": "allow-app-rules", + "properties": { + "action": { + "type": "allow" + }, + "priority": 100, + "rules": [ + { + "fqdnTags": [ + "AppServiceEnvironment", + "WindowsUpdate" + ], + "name": "allow-ase-tags", + "protocols": [ + { + "port": "80", + "protocolType": "HTTP" + }, + { + "port": "443", + "protocolType": "HTTPS" + } + ], + "sourceAddresses": [ + "*" + ] + }, + { + "name": "allow-ase-management", + "protocols": [ + { + "port": "80", + "protocolType": "HTTP" + }, + { + "port": "443", + "protocolType": "HTTPS" + } + ], + "sourceAddresses": [ + "*" + ], + "targetFqdns": [ + "management.azure.com" + ] + } + ] + } + } + ] + }, + "azureFirewallSubnetPublicIpId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkRuleCollections": { + "value": [ + { + "name": "allow-network-rules", + "properties": { + "action": { + "type": "allow" + }, + "priority": 100, + "rules": [ + { + "destinationAddresses": [ + "*" + ], + "destinationPorts": [ + "12000", + "123" + ], + "name": "allow-ntp", + "protocols": [ + "Any" + ], + "sourceAddresses": [ + "*" + ] + } + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/azureFirewalls/version.json b/modules/Microsoft.Network/azureFirewalls/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/azureFirewalls/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..8769fe7 --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'ExpressRoute Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7896-14b4-4889-afef-fbb65a96e5a2') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 azureBastion 'Microsoft.Network/bastionHosts@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(azureBastion.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: azureBastion +}] diff --git a/modules/Microsoft.Network/bastionHosts/.test/custompip.parameters.json b/modules/Microsoft.Network/bastionHosts/.test/custompip.parameters.json new file mode 100644 index 0000000..dbb195e --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/.test/custompip.parameters.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-bas-custompip-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-bas" + }, + "publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-bas", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } + } + } +} diff --git a/modules/Microsoft.Network/bastionHosts/.test/min.parameters.json b/modules/Microsoft.Network/bastionHosts/.test/min.parameters.json new file mode 100644 index 0000000..6ef3885 --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-bas-min-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002" + } + } +} diff --git a/modules/Microsoft.Network/bastionHosts/.test/parameters.json b/modules/Microsoft.Network/bastionHosts/.test/parameters.json new file mode 100644 index 0000000..a00daef --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/.test/parameters.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-bas-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + "azureBastionSubnetPublicIpId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-bas" + }, + "skuType": { + "value": "Standard" + }, + "scaleUnits": { + "value": 4 + }, + "disableCopyPaste": { + "value": true + }, + "enableFileCopy": { + "value": false + }, + "enableIpConnect": { + "value": false + }, + "enableShareableLink": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/bastionHosts/deploy.bicep b/modules/Microsoft.Network/bastionHosts/deploy.bicep new file mode 100644 index 0000000..219b475 --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/deploy.bicep @@ -0,0 +1,243 @@ +@description('Required. Name of the Azure Bastion resource.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Shared services Virtual Network resource identifier.') +param vNetId string + +@description('Optional. The public ip resource ID to associate to the azureBastionSubnet. If empty, then the public ip that is created as part of this module will be applied to the azureBastionSubnet.') +param azureBastionSubnetPublicIpId string = '' + +@description('Optional. Specifies if a public ip should be created by default if one is not provided.') +param isCreateDefaultPublicIP bool = true + +@description('Optional. Specifies the properties of the public IP to create and be used by Azure Bastion. If it\'s not provided and publicIPAddressResourceId is empty, a \'-pip\' suffix will be appended to the Bastion\'s name.') +param publicIPAddressObject object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@allowed([ + 'Basic' + 'Standard' +]) +@description('Optional. The SKU of this Bastion Host.') +param skuType string = 'Basic' + +@description('Optional. Choose to disable or enable Copy Paste.') +param disableCopyPaste bool = false + +@description('Optional. Choose to disable or enable File Copy.') +param enableFileCopy bool = true + +@description('Optional. Choose to disable or enable IP Connect.') +param enableIpConnect bool = false + +@description('Optional. Choose to disable or enable Shareable Link.') +param enableShareableLink bool = false + +@description('Optional. The scale units for the Bastion Host resource.') +param scaleUnits int = 2 + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Optional. The name of bastion logs that will be streamed.') +@allowed([ + 'BastionAuditLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'BastionAuditLogs' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableTunneling = skuType == 'Standard' ? true : null + +var scaleUnits_var = skuType == 'Basic' ? 2 : scaleUnits + +// ---------------------------------------------------------------------------- +// Prep ipConfigurations object AzureBastionSubnet for different uses cases: +// 1. Use existing public ip +// 2. Use new public ip created in this module +// 3. Do not use a public ip if isCreateDefaultPublicIP is false +var subnet_var = { + subnet: { + id: '${vNetId}/subnets/AzureBastionSubnet' // The subnet name must be AzureBastionSubnet + } +} +var existingPip = { + publicIPAddress: { + id: azureBastionSubnetPublicIpId + } +} +var newPip = { + publicIPAddress: (empty(azureBastionSubnetPublicIpId) && isCreateDefaultPublicIP) ? { + id: publicIPAddress.outputs.resourceId + } : null +} + +var ipConfigurations = [ + { + name: 'IpConfAzureBastionSubnet' + //Use existing public ip, new public ip created in this module, or none if isCreateDefaultPublicIP is false + properties: union(subnet_var, !empty(azureBastionSubnetPublicIpId) ? existingPip : {}, (isCreateDefaultPublicIP ? newPip : {})) + } +] + +// ---------------------------------------------------------------------------- + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module publicIPAddress '../publicIPAddresses/deploy.bicep' = if (empty(azureBastionSubnetPublicIpId) && isCreateDefaultPublicIP) { + name: '${uniqueString(deployment().name, location)}-Bastion-PIP' + params: { + name: contains(publicIPAddressObject, 'name') ? publicIPAddressObject.name : '${name}-pip' + diagnosticLogCategoriesToEnable: contains(publicIPAddressObject, 'diagnosticLogCategoriesToEnable') ? publicIPAddressObject.diagnosticLogCategoriesToEnable : [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] + diagnosticMetricsToEnable: contains(publicIPAddressObject, 'diagnosticMetricsToEnable') ? publicIPAddressObject.diagnosticMetricsToEnable : [ + 'AllMetrics' + ] + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + enableDefaultTelemetry: enableDefaultTelemetry + location: location + lock: lock + publicIPAddressVersion: contains(publicIPAddressObject, 'publicIPAddressVersion') ? publicIPAddressObject.publicIPAddressVersion : 'IPv4' + publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod') ? publicIPAddressObject.publicIPAllocationMethod : 'Static' + publicIPPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId') ? publicIPAddressObject.publicIPPrefixResourceId : '' + roleAssignments: contains(publicIPAddressObject, 'roleAssignments') ? publicIPAddressObject.roleAssignments : [] + skuName: contains(publicIPAddressObject, 'skuName') ? publicIPAddressObject.skuName : 'Standard' + skuTier: contains(publicIPAddressObject, 'skuTier') ? publicIPAddressObject.skuTier : 'Regional' + tags: tags + zones: contains(publicIPAddressObject, 'zones') ? publicIPAddressObject.zones : [] + } +} + +var bastionproperties_var = skuType == 'Standard' ? { + scaleUnits: scaleUnits_var + ipConfigurations: ipConfigurations + enableTunneling: enableTunneling + disableCopyPaste: disableCopyPaste + enableFileCopy: enableFileCopy + enableIpConnect: enableIpConnect + enableShareableLink: enableShareableLink +} : { + scaleUnits: scaleUnits_var + ipConfigurations: ipConfigurations +} + +resource azureBastion 'Microsoft.Network/bastionHosts@2022-01-01' = { + name: name + location: location + tags: tags + sku: { + name: skuType + } + properties: bastionproperties_var +} + +resource azureBastion_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${azureBastion.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: azureBastion +} + +resource azureBastion_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: azureBastion +} + +module azureBastion_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Bastion-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: azureBastion.id + } +}] + +@description('The resource group the Azure Bastion was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name the Azure Bastion.') +output name string = azureBastion.name + +@description('The resource ID the Azure Bastion.') +output resourceId string = azureBastion.id + +@description('The location the resource was deployed into.') +output location string = azureBastion.location + +@description('The public ipconfiguration object for the AzureBastionSubnet.') +output ipConfAzureBastionSubnet object = azureBastion.properties.ipConfigurations[0] diff --git a/modules/Microsoft.Network/bastionHosts/readme.md b/modules/Microsoft.Network/bastionHosts/readme.md new file mode 100644 index 0000000..854f8cc --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/readme.md @@ -0,0 +1,552 @@ +# Bastion Hosts `[Microsoft.Network/bastionHosts]` + +This module deploys a bastion host. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/bastionHosts` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-01-01/bastionHosts) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Bastion resource. | +| `vNetId` | string | Shared services Virtual Network resource identifier. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `azureBastionSubnetPublicIpId` | string | `''` | | The public ip resource ID to associate to the azureBastionSubnet. If empty, then the public ip that is created as part of this module will be applied to the azureBastionSubnet. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[BastionAuditLogs]` | `[BastionAuditLogs]` | Optional. The name of bastion logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableCopyPaste` | bool | `False` | | Choose to disable or enable Copy Paste. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableFileCopy` | bool | `True` | | Choose to disable or enable File Copy. | +| `enableIpConnect` | bool | `False` | | Choose to disable or enable IP Connect. | +| `enableShareableLink` | bool | `False` | | Choose to disable or enable Shareable Link. | +| `isCreateDefaultPublicIP` | bool | `True` | | Specifies if a public ip should be created by default if one is not provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicIPAddressObject` | object | `{object}` | | Specifies the properties of the public IP to create and be used by Azure Bastion. If it's not provided and publicIPAddressResourceId is empty, a '-pip' suffix will be appended to the Bastion's name. | +| `roleAssignments` | array | `[]` | | 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'. | +| `scaleUnits` | int | `2` | | The scale units for the Bastion Host resource. | +| `skuType` | string | `'Basic'` | `[Basic, Standard]` | The SKU of this Bastion Host. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `additionalPublicIpConfigurations` + +Create additional public ip configurations from existing public ips + +

+ +Parameter JSON format + +```json +"additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01" + }, + { + "name": "ipConfig02", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01' + } + { + name: 'ipConfig02' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02' + } +] +``` + +
+ + +### Parameter Usage: `publicIPAddressObject` + +The Public IP Address object to create as part of the module. This will be created if `isCreateDefaultPublicIP` is true (which it is by default). If not provided, the name and other configurations will be set by default. + + +
+ +Parameter JSON format + +```json +"publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } +} +``` + +
+ + + +
+ +Bicep format + + +```bicep +publicIPAddressObject: { + name: 'mypip' + publicIPPrefixResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPPrefixes/myprefix' + publicIPAllocationMethod: 'Dynamic' + skuName: 'Basic' + skuTier: 'Regional' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '<>' + ] + } + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] +} +``` + +
+ + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +
+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `ipConfAzureBastionSubnet` | object | The public ipconfiguration object for the AzureBastionSubnet. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name the Azure Bastion. | +| `resourceGroupName` | string | The resource group the Azure Bastion was deployed into. | +| `resourceId` | string | The resource ID the Azure Bastion. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Custompip

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BastionHosts' + params: { + // Required parameters + name: '<>-az-bas-custompip-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-bas' + // Non-required parameters + publicIPAddressObject: { + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: 'adp-<>-az-pip-custom-x-bas' + publicIPAllocationMethod: 'Static' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-bas-custompip-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-custompip-bas" + }, + // Non-required parameters + "publicIPAddressObject": { + "value": { + "diagnosticLogCategoriesToEnable": [ + "DDoSMitigationFlowLogs", + "DDoSMitigationReports", + "DDoSProtectionNotifications" + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "name": "adp-<>-az-pip-custom-x-bas", + "publicIPAllocationMethod": "Static", + "publicIPPrefixResourceId": "", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "skuName": "Standard", + "skuTier": "Regional" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BastionHosts' + params: { + // Required parameters + name: '<>-az-bas-min-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002' + } +} +``` + +
+

+ +

+ +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": "<>-az-bas-min-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-BastionHosts' + params: { + // Required parameters + name: '<>-az-bas-x-001' + vNetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001' + // Non-required parameters + azureBastionSubnetPublicIpId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-bas' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + scaleUnits: 4 + skuType: 'Standard' + } +} +``` + +
+

+ +

+ +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": "<>-az-bas-x-001" + }, + "vNetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + // Non-required parameters + "azureBastionSubnetPublicIpId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-bas" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disableCopyPaste": { + "value": true + }, + "enableFileCopy": { + "value": false + }, + "enableIpConnect": { + "value": false + }, + "enableShareableLink": { + "value": false + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scaleUnits": { + "value": 4 + }, + "skuType": { + "value": "Standard" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/bastionHosts/version.json b/modules/Microsoft.Network/bastionHosts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/bastionHosts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/connections/.test/vnet2vnet.parameters.json b/modules/Microsoft.Network/connections/.test/vnet2vnet.parameters.json new file mode 100644 index 0000000..c58d1a4 --- /dev/null +++ b/modules/Microsoft.Network/connections/.test/vnet2vnet.parameters.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vnetgwc-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "virtualNetworkGateway1": { + "value": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-001" + } + }, + "virtualNetworkGateway2": { + "value": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-002" + } + }, + "vpnSharedKey": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "vpnSharedKey" + } + }, + "virtualNetworkGatewayConnectionType": { + "value": "Vnet2Vnet" + }, + "enableBgp": { + "value": false + }, + "location": { + "value": "eastus" + } + } +} diff --git a/modules/Microsoft.Network/connections/deploy.bicep b/modules/Microsoft.Network/connections/deploy.bicep new file mode 100644 index 0000000..c1b0acf --- /dev/null +++ b/modules/Microsoft.Network/connections/deploy.bicep @@ -0,0 +1,128 @@ +@description('Required. Remote connection name.') +param name string + +@description('Optional. Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways.') +param vpnSharedKey string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Gateway connection type.') +@allowed([ + 'IPsec' + 'Vnet2Vnet' + 'ExpressRoute' + 'VPNClient' +]) +param virtualNetworkGatewayConnectionType string = 'IPsec' + +@description('Optional. Value to specify if BGP is enabled or not.') +param enableBgp bool = false + +@description('Optional. Enable policy-based traffic selectors.') +param usePolicyBasedTrafficSelectors bool = false + +@description('Optional. The IPSec Policies to be considered by this connection.') +param customIPSecPolicy object = { + saLifeTimeSeconds: 0 + saDataSizeKilobytes: 0 + ipsecEncryption: '' + ipsecIntegrity: '' + ikeEncryption: '' + ikeIntegrity: '' + dhGroup: '' + pfsGroup: '' +} + +@description('Optional. The weight added to routes learned from this BGP speaker.') +param routingWeight int = -1 + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. The primary Virtual Network Gateway.') +param virtualNetworkGateway1 object + +@description('Optional. The remote Virtual Network Gateway. Used for connection type [Vnet2Vnet].') +param virtualNetworkGateway2 object = {} + +@description('Optional. The remote peer. Used for connection type [ExpressRoute].') +param peer object = {} + +@description('Optional. The local network gateway. Used for connection type [IPsec].') +param localNetworkGateway2 object = {} + +var customIPSecPolicy_var = [ + { + saLifeTimeSeconds: customIPSecPolicy.saLifeTimeSeconds + saDataSizeKilobytes: customIPSecPolicy.saDataSizeKilobytes + ipsecEncryption: customIPSecPolicy.ipsecEncryption + ipsecIntegrity: customIPSecPolicy.ipsecIntegrity + ikeEncryption: customIPSecPolicy.ikeEncryption + ikeIntegrity: customIPSecPolicy.ikeIntegrity + dhGroup: customIPSecPolicy.dhGroup + pfsGroup: customIPSecPolicy.pfsGroup + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource connection 'Microsoft.Network/connections@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + connectionType: virtualNetworkGatewayConnectionType + virtualNetworkGateway1: virtualNetworkGateway1 + virtualNetworkGateway2: virtualNetworkGatewayConnectionType == 'Vnet2Vnet' ? virtualNetworkGateway2 : null + localNetworkGateway2: virtualNetworkGatewayConnectionType == 'IPsec' ? localNetworkGateway2 : null + peer: virtualNetworkGatewayConnectionType == 'ExpressRoute' ? peer : null + sharedKey: virtualNetworkGatewayConnectionType != 'ExpressRoute' ? vpnSharedKey : null + usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors + ipsecPolicies: !empty(customIPSecPolicy.ipsecEncryption) ? customIPSecPolicy_var : customIPSecPolicy.ipsecEncryption + routingWeight: routingWeight != -1 ? routingWeight : null + enableBgp: enableBgp + } +} + +resource connection_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${connection.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: connection +} + +@description('The resource group the remote connection was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the remote connection.') +output name string = connection.name + +@description('The resource ID of the remote connection.') +output resourceId string = connection.id + +@description('The location the resource was deployed into.') +output location string = connection.location diff --git a/modules/Microsoft.Network/connections/readme.md b/modules/Microsoft.Network/connections/readme.md new file mode 100644 index 0000000..9106681 --- /dev/null +++ b/modules/Microsoft.Network/connections/readme.md @@ -0,0 +1,402 @@ +# Virtual Network Gateway Connections `[Microsoft.Network/connections]` + +This template deploys a virtual network gateway connection. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Network/connections` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/connections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Remote connection name. | +| `virtualNetworkGateway1` | object | The primary Virtual Network Gateway. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `customIPSecPolicy` | object | `{object}` | | The IPSec Policies to be considered by this connection. | +| `enableBgp` | bool | `False` | | Value to specify if BGP is enabled or not. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `localNetworkGateway2` | object | `{object}` | | The local network gateway. Used for connection type [IPsec]. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `peer` | object | `{object}` | | The remote peer. Used for connection type [ExpressRoute]. | +| `routingWeight` | int | `-1` | | The weight added to routes learned from this BGP speaker. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `usePolicyBasedTrafficSelectors` | bool | `False` | | Enable policy-based traffic selectors. | +| `virtualNetworkGateway2` | object | `{object}` | | The remote Virtual Network Gateway. Used for connection type [Vnet2Vnet]. | +| `virtualNetworkGatewayConnectionType` | string | `'IPsec'` | `[ExpressRoute, IPsec, Vnet2Vnet, VPNClient]` | Gateway connection type. | +| `vpnSharedKey` | string | `''` | | Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways. | + + +### Parameter Usage: `virtualNetworkGateway1` + +The primary virtual network gateway object. + +

+ +Parameter JSON format + +```json +"virtualNetworkGateway1": { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway01" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkGateway1: { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway01' +} +``` + +
+

+ +### Parameter Usage: `virtualNetworkGateway2` + +The secondary virtual network gateway used for VNET to VNET connections. + +

+ +Parameter JSON format + +```json +"virtualNetworkGateway2" : { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway02" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkGateway2 : { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway02' +} +``` + +
+

+ +### Parameter Usage: `localNetworkGateway2` + +The local virtual network gateway object. + +

+ +Parameter JSON format + +```json +"localNetworkGateway2": { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +localNetworkGateway2: { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway' +} +``` + +
+

+ +### Parameter Usage: `peer` + +The remote peer object used for ExpressRoute connections + +

+ +Parameter JSON format + +```json +"peer": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute" +} +``` + +
+ +
+ +Bicep format + +```bicep +'peer': { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute' +} +``` + +
+

+ +### Parameter Usage: `customIPSecPolicy` + +If ipsecEncryption parameter is empty, customIPSecPolicy will not be deployed. The parameter file should look like below. + +

+ +Parameter JSON format + +```json +"customIPSecPolicy": { + "value": { + "saLifeTimeSeconds": 0, + "saDataSizeKilobytes": 0, + "ipsecEncryption": "", + "ipsecIntegrity": "", + "ikeEncryption": "", + "ikeIntegrity": "", + "dhGroup": "", + "pfsGroup": "" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +customIPSecPolicy: { + saLifeTimeSeconds: 0 + saDataSizeKilobytes: 0 + ipsecEncryption: '' + ipsecIntegrity: '' + ikeEncryption: '' + ikeIntegrity: '' + dhGroup: '' + pfsGroup: '' +} +``` + +
+

+ +Format of the full customIPSecPolicy parameter in parameter file. + +

+ +Parameter JSON format + +```json +"customIPSecPolicy": { + "value": { + "saLifeTimeSeconds": 28800, + "saDataSizeKilobytes": 102400000, + "ipsecEncryption": "AES256", + "ipsecIntegrity": "SHA256", + "ikeEncryption": "AES256", + "ikeIntegrity": "SHA256", + "dhGroup": "DHGroup14", + "pfsGroup": "None" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +customIPSecPolicy: { + saLifeTimeSeconds: 28800 + saDataSizeKilobytes: 102400000 + ipsecEncryption: 'AES256' + ipsecIntegrity: 'SHA256' + ikeEncryption: 'AES256' + ikeIntegrity: 'SHA256' + dhGroup: 'DHGroup14' + pfsGroup: 'None' +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the remote connection. | +| `resourceGroupName` | string | The resource group the remote connection was deployed into. | +| `resourceId` | string | The resource ID of the remote connection. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Vnet2vnet

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module connections './Microsoft.Network/connections/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Connections' + params: { + // Required parameters + name: '<>-az-vnetgwc-x-001' + virtualNetworkGateway1: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-001' + } + // Non-required parameters + enableBgp: false + location: 'eastus' + lock: 'CanNotDelete' + virtualNetworkGateway2: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-002' + } + virtualNetworkGatewayConnectionType: 'Vnet2Vnet' + vpnSharedKey: kv1.getSecret('vpnSharedKey') + } +} +``` + +
+

+ +

+ +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": "<>-az-vnetgwc-x-001" + }, + "virtualNetworkGateway1": { + "value": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-001" + } + }, + // Non-required parameters + "enableBgp": { + "value": false + }, + "location": { + "value": "eastus" + }, + "lock": { + "value": "CanNotDelete" + }, + "virtualNetworkGateway2": { + "value": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworkGateways/<>-az-vnet-vpn-gw-p-002" + } + }, + "virtualNetworkGatewayConnectionType": { + "value": "Vnet2Vnet" + }, + "vpnSharedKey": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "vpnSharedKey" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/connections/version.json b/modules/Microsoft.Network/connections/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/connections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..7fd47de --- /dev/null +++ b/modules/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(ddosProtectionPlan.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: ddosProtectionPlan +}] diff --git a/modules/Microsoft.Network/ddosProtectionPlans/.test/parameters.json b/modules/Microsoft.Network/ddosProtectionPlans/.test/parameters.json new file mode 100644 index 0000000..fe639af --- /dev/null +++ b/modules/Microsoft.Network/ddosProtectionPlans/.test/parameters.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ddos-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/ddosProtectionPlans/deploy.bicep b/modules/Microsoft.Network/ddosProtectionPlans/deploy.bicep new file mode 100644 index 0000000..75e4c03 --- /dev/null +++ b/modules/Microsoft.Network/ddosProtectionPlans/deploy.bicep @@ -0,0 +1,76 @@ +@description('Required. Name of the DDoS protection plan to assign the VNET to.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2021-08-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource ddosProtectionPlan_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${ddosProtectionPlan.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: ddosProtectionPlan +} + +module ddosProtectionPlan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-DDoSProtectionPlan-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: ddosProtectionPlan.id + } +}] + +@description('The resource group the DDOS protection plan was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the DDOS protection plan.') +output resourceId string = ddosProtectionPlan.id + +@description('The name of the DDOS protection plan.') +output name string = ddosProtectionPlan.name + +@description('The location the resource was deployed into.') +output location string = ddosProtectionPlan.location diff --git a/modules/Microsoft.Network/ddosProtectionPlans/readme.md b/modules/Microsoft.Network/ddosProtectionPlans/readme.md new file mode 100644 index 0000000..55fe2f2 --- /dev/null +++ b/modules/Microsoft.Network/ddosProtectionPlans/readme.md @@ -0,0 +1,219 @@ +# DDoS Protection Plans `[Microsoft.Network/ddosProtectionPlans]` + +This template deploys a DDoS protection plan. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/ddosProtectionPlans` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/ddosProtectionPlans) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the DDoS protection plan to assign the VNET to. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the DDOS protection plan. | +| `resourceGroupName` | string | The resource group the DDOS protection plan was deployed into. | +| `resourceId` | string | The resource ID of the DDOS protection plan. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module ddosProtectionPlans './Microsoft.Network/ddosProtectionPlans/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DdosProtectionPlans' + params: { + // Required parameters + name: '<>-az-ddos-x-001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-ddos-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/ddosProtectionPlans/version.json b/modules/Microsoft.Network/ddosProtectionPlans/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/ddosProtectionPlans/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..2348dfc --- /dev/null +++ b/modules/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(expressRouteCircuits.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: expressRouteCircuits +}] diff --git a/modules/Microsoft.Network/expressRouteCircuits/.test/parameters.json b/modules/Microsoft.Network/expressRouteCircuits/.test/parameters.json new file mode 100644 index 0000000..fa42098 --- /dev/null +++ b/modules/Microsoft.Network/expressRouteCircuits/.test/parameters.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-erc-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "serviceProviderName": { + "value": "Equinix" + }, + "peeringLocation": { + "value": "Amsterdam" + }, + "bandwidthInMbps": { + "value": 50 + }, + "skuTier": { + "value": "Standard" + }, + "skuFamily": { + "value": "MeteredData" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/expressRouteCircuits/deploy.bicep b/modules/Microsoft.Network/expressRouteCircuits/deploy.bicep new file mode 100644 index 0000000..1fe5c08 --- /dev/null +++ b/modules/Microsoft.Network/expressRouteCircuits/deploy.bicep @@ -0,0 +1,225 @@ +@description('Required. This is the name of the ExpressRoute circuit.') +param name string + +@description('Required. This is the name of the ExpressRoute Service Provider. It must exactly match one of the Service Providers from List ExpressRoute Service Providers API call.') +param serviceProviderName string + +@description('Required. This is the name of the peering location and not the ARM resource location. It must exactly match one of the available peering locations from List ExpressRoute Service Providers API call.') +param peeringLocation string + +@description('Required. This is the bandwidth in Mbps of the circuit being created. It must exactly match one of the available bandwidth offers List ExpressRoute Service Providers API call.') +param bandwidthInMbps int + +@description('Optional. Chosen SKU Tier of ExpressRoute circuit. Choose from Local, Premium or Standard SKU tiers.') +@allowed([ + 'Local' + 'Standard' + 'Premium' +]) +param skuTier string = 'Standard' + +@description('Optional. Chosen SKU family of ExpressRoute circuit. Choose from MeteredData or UnlimitedData SKU families.') +@allowed([ + 'MeteredData' + 'UnlimitedData' +]) +param skuFamily string = 'MeteredData' + +@description('Optional. Enabled BGP peering type for the Circuit.') +@allowed([ + true + false +]) +param peering bool = false + +@description('Optional. BGP peering type for the Circuit. Choose from AzurePrivatePeering, AzurePublicPeering or MicrosoftPeering.') +@allowed([ + 'AzurePrivatePeering' + 'MicrosoftPeering' +]) +param peeringType string = 'AzurePrivatePeering' + +@description('Optional. The shared key for peering configuration. Router does MD5 hash comparison to validate the packets sent by BGP connection. This parameter is optional and can be removed from peering configuration if not required.') +param sharedKey string = '' + +@description('Optional. The autonomous system number of the customer/connectivity provider.') +param peerASN int = 0 + +@description('Optional. A /30 subnet used to configure IP addresses for interfaces on Link1.') +param primaryPeerAddressPrefix string = '' + +@description('Optional. A /30 subnet used to configure IP addresses for interfaces on Link2.') +param secondaryPeerAddressPrefix string = '' + +@description('Optional. Specifies the identifier that is used to identify the customer.') +param vlanId int = 0 + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'PeeringRouteLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'PeeringRouteLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var peeringConfiguration = [ + { + name: peeringType + properties: { + peeringType: peeringType + sharedKey: sharedKey + peerASN: peerASN + primaryPeerAddressPrefix: primaryPeerAddressPrefix + secondaryPeerAddressPrefix: secondaryPeerAddressPrefix + vlanId: vlanId + } + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: '${skuTier}_${skuFamily}' + tier: skuTier + family: skuTier == 'Local' ? 'UnlimitedData' : skuFamily + } + properties: { + serviceProviderProperties: { + serviceProviderName: serviceProviderName + peeringLocation: peeringLocation + bandwidthInMbps: bandwidthInMbps + } + peerings: peering ? peeringConfiguration : null + } +} + +resource expressRouteCircuits_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${expressRouteCircuits.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: expressRouteCircuits +} + +resource expressRouteCircuits_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: expressRouteCircuits +} + +module expressRouteCircuits_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ExpRouteCircuits-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: expressRouteCircuits.id + } +}] + +@description('The resource ID of express route curcuit.') +output resourceId string = expressRouteCircuits.id + +@description('The resource group the express route curcuit was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of express route curcuit.') +output name string = expressRouteCircuits.name + +@description('The service key of the express route circuit.') +output serviceKey string = reference(expressRouteCircuits.id, '2021-02-01').serviceKey + +@description('The location the resource was deployed into.') +output location string = expressRouteCircuits.location diff --git a/modules/Microsoft.Network/expressRouteCircuits/readme.md b/modules/Microsoft.Network/expressRouteCircuits/readme.md new file mode 100644 index 0000000..201f243 --- /dev/null +++ b/modules/Microsoft.Network/expressRouteCircuits/readme.md @@ -0,0 +1,281 @@ +# ExpressRoute Circuits `[Microsoft.Network/expressRouteCircuits]` + +This template deploys an express route circuit. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/expressRouteCircuits` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/expressRouteCircuits) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `bandwidthInMbps` | int | This is the bandwidth in Mbps of the circuit being created. It must exactly match one of the available bandwidth offers List ExpressRoute Service Providers API call. | +| `name` | string | This is the name of the ExpressRoute circuit. | +| `peeringLocation` | string | This is the name of the peering location and not the ARM resource location. It must exactly match one of the available peering locations from List ExpressRoute Service Providers API call. | +| `serviceProviderName` | string | This is the name of the ExpressRoute Service Provider. It must exactly match one of the Service Providers from List ExpressRoute Service Providers API call. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[PeeringRouteLog]` | `[PeeringRouteLog]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `peerASN` | int | `0` | | The autonomous system number of the customer/connectivity provider. | +| `peering` | bool | `False` | `[False, True]` | Enabled BGP peering type for the Circuit. | +| `peeringType` | string | `'AzurePrivatePeering'` | `[AzurePrivatePeering, MicrosoftPeering]` | BGP peering type for the Circuit. Choose from AzurePrivatePeering, AzurePublicPeering or MicrosoftPeering. | +| `primaryPeerAddressPrefix` | string | `''` | | A /30 subnet used to configure IP addresses for interfaces on Link1. | +| `roleAssignments` | array | `[]` | | 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'. | +| `secondaryPeerAddressPrefix` | string | `''` | | A /30 subnet used to configure IP addresses for interfaces on Link2. | +| `sharedKey` | string | `''` | | The shared key for peering configuration. Router does MD5 hash comparison to validate the packets sent by BGP connection. This parameter is optional and can be removed from peering configuration if not required. | +| `skuFamily` | string | `'MeteredData'` | `[MeteredData, UnlimitedData]` | Chosen SKU family of ExpressRoute circuit. Choose from MeteredData or UnlimitedData SKU families. | +| `skuTier` | string | `'Standard'` | `[Local, Premium, Standard]` | Chosen SKU Tier of ExpressRoute circuit. Choose from Local, Premium or Standard SKU tiers. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vlanId` | int | `0` | | Specifies the identifier that is used to identify the customer. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of express route curcuit. | +| `resourceGroupName` | string | The resource group the express route curcuit was deployed into. | +| `resourceId` | string | The resource ID of express route curcuit. | +| `serviceKey` | string | The service key of the express route circuit. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module expressRouteCircuits './Microsoft.Network/expressRouteCircuits/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ExpressRouteCircuits' + params: { + // Required parameters + bandwidthInMbps: 50 + name: '<>-az-erc-x-001' + peeringLocation: 'Amsterdam' + serviceProviderName: 'Equinix' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuFamily: 'MeteredData' + skuTier: 'Standard' + } +} +``` + +
+

+ +

+ +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 + "bandwidthInMbps": { + "value": 50 + }, + "name": { + "value": "<>-az-erc-x-001" + }, + "peeringLocation": { + "value": "Amsterdam" + }, + "serviceProviderName": { + "value": "Equinix" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuFamily": { + "value": "MeteredData" + }, + "skuTier": { + "value": "Standard" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/expressRouteCircuits/version.json b/modules/Microsoft.Network/expressRouteCircuits/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/expressRouteCircuits/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/firewallPolicies/.test/min.parameters.json b/modules/Microsoft.Network/firewallPolicies/.test/min.parameters.json new file mode 100644 index 0000000..bb55508 --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/.test/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": "<>-az-fwpol-min-001" + } + } +} diff --git a/modules/Microsoft.Network/firewallPolicies/.test/parameters.json b/modules/Microsoft.Network/firewallPolicies/.test/parameters.json new file mode 100644 index 0000000..67e03ad --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/.test/parameters.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fwpol-x-002" + }, + "ruleCollectionGroups": { + "value": [ + { + "name": "<>-rule-001", + "priority": 5000, + "ruleCollections": [ + { + "name": "collection002", + "priority": 5555, + "action": { + "type": "Allow" + }, + "rules": [ + { + "name": "rule002", + "ipProtocols": [ + "TCP", + "UDP" + ], + "destinationPorts": [ + "80" + ], + "sourceAddresses": [ + "*" + ], + "sourceIpGroups": [], + "ruleType": "NetworkRule", + "destinationIpGroups": [], + "destinationAddresses": [ + "*" + ], + "destinationFqdns": [] + } + ], + "ruleCollectionType": "FirewallPolicyFilterRuleCollection" + } + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/firewallPolicies/deploy.bicep b/modules/Microsoft.Network/firewallPolicies/deploy.bicep new file mode 100644 index 0000000..04faef5 --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/deploy.bicep @@ -0,0 +1,185 @@ +@description('Required. Name of the Firewall Policy.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Firewall policy resource.') +param tags object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Resource ID of the base policy.') +param basePolicyResourceId string = '' + +@description('Optional. Enable DNS Proxy on Firewalls attached to the Firewall Policy.') +param enableProxy bool = false + +@description('Optional. List of Custom DNS Servers.') +param servers array = [] + +@description('Optional. A flag to indicate if the insights are enabled on the policy.') +param insightsIsEnabled bool = false + +@description('Optional. Default Log Analytics Resource ID for Firewall Policy Insights.') +param defaultWorkspaceId string = '' + +@description('Optional. List of workspaces for Firewall Policy Insights.') +param workspaces array = [] + +@description('Optional. Number of days the insights should be enabled on the policy.') +param retentionDays int = 365 + +@description('Optional. List of rules for traffic to bypass.') +param bypassTrafficSettings array = [] + +@description('Optional. List of specific signatures states.') +param signatureOverrides array = [] + +@description('Optional. The configuring of intrusion detection.') +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param mode string = 'Off' + +@description('Optional. Tier of Firewall Policy.') +@allowed([ + 'Premium' + 'Standard' +]) +param tier string = 'Standard' + +@description('Optional. List of private IP addresses/IP address ranges to not be SNAT.') +param privateRanges array = [] + +@description('Optional. The operation mode for Threat Intel.') +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param threatIntelMode string = 'Off' + +@description('Optional. List of FQDNs for the ThreatIntel Allowlist.') +param fqdns array = [] + +@description('Optional. List of IP addresses for the ThreatIntel Allowlist.') +param ipAddresses array = [] + +@description('Optional. Secret ID of (base-64 encoded unencrypted PFX) Secret or Certificate object stored in KeyVault.') +#disable-next-line secure-secrets-in-params // Not a secret +param keyVaultSecretId string = '' + +@description('Optional. Name of the CA certificate.') +param certificateName string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Rule collection groups.') +param ruleCollectionGroups array = [] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2021-08-01' = { + name: name + location: location + tags: tags + identity: identity + properties: { + basePolicy: !empty(basePolicyResourceId) ? { + id: basePolicyResourceId + } : null + dnsSettings: enableProxy ? { + enableProxy: enableProxy + servers: servers + } : null + insights: insightsIsEnabled ? { + isEnabled: insightsIsEnabled + logAnalyticsResources: { + defaultWorkspaceId: { + id: !empty(defaultWorkspaceId) ? defaultWorkspaceId : null + } + workspaces: !empty(workspaces) ? workspaces : null + } + retentionDays: retentionDays + } : null + intrusionDetection: (mode != 'Off') ? { + configuration: { + bypassTrafficSettings: !empty(bypassTrafficSettings) ? bypassTrafficSettings : null + signatureOverrides: !empty(signatureOverrides) ? signatureOverrides : null + } + mode: mode + } : null + sku: { + tier: tier + } + snat: !empty(privateRanges) ? { + privateRanges: privateRanges + } : null + threatIntelMode: threatIntelMode + threatIntelWhitelist: { + fqdns: fqdns + ipAddresses: ipAddresses + } + transportSecurity: (!empty(keyVaultSecretId) || !empty(certificateName)) ? { + certificateAuthority: { + keyVaultSecretId: !empty(keyVaultSecretId) ? keyVaultSecretId : null + name: !empty(certificateName) ? certificateName : null + } + } : null + } +} + +// When a FW policy uses a base policy and have more rule collection groups, +// they need to be deployed sequentially, otherwise the deployment would fail +// because of concurrent access to the base policy. +// The next line forces ARM to deploy them one after the other, so no race concition on the base policy will happen. +@batchSize(1) +module firewallPolicy_ruleCollectionGroups 'ruleCollectionGroups/deploy.bicep' = [for (ruleCollectionGroup, index) in ruleCollectionGroups: { + name: '${uniqueString(deployment().name, location)}-firewallPolicy_ruleCollectionGroups-${index}' + params: { + firewallPolicyName: firewallPolicy.name + name: ruleCollectionGroup.name + priority: ruleCollectionGroup.priority + ruleCollections: ruleCollectionGroup.ruleCollections + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed firewall policy.') +output name string = firewallPolicy.name + +@description('The resource ID of the deployed firewall policy.') +output resourceId string = firewallPolicy.id + +@description('The resource group of the deployed firewall policy.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = firewallPolicy.location diff --git a/modules/Microsoft.Network/firewallPolicies/readme.md b/modules/Microsoft.Network/firewallPolicies/readme.md new file mode 100644 index 0000000..cfc10fd --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/readme.md @@ -0,0 +1,303 @@ +# Firewall Policies `[Microsoft.Network/firewallPolicies]` + +This module deploys Firewall Policies. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/firewallPolicies` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/firewallPolicies) | +| `Microsoft.Network/firewallPolicies/ruleCollectionGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/firewallPolicies/ruleCollectionGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Firewall Policy. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `basePolicyResourceId` | string | `''` | | Resource ID of the base policy. | +| `bypassTrafficSettings` | array | `[]` | | List of rules for traffic to bypass. | +| `certificateName` | string | `''` | | Name of the CA certificate. | +| `defaultWorkspaceId` | string | `''` | | Default Log Analytics Resource ID for Firewall Policy Insights. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableProxy` | bool | `False` | | Enable DNS Proxy on Firewalls attached to the Firewall Policy. | +| `fqdns` | array | `[]` | | List of FQDNs for the ThreatIntel Allowlist. | +| `insightsIsEnabled` | bool | `False` | | A flag to indicate if the insights are enabled on the policy. | +| `ipAddresses` | array | `[]` | | List of IP addresses for the ThreatIntel Allowlist. | +| `keyVaultSecretId` | string | `''` | | Secret ID of (base-64 encoded unencrypted PFX) Secret or Certificate object stored in KeyVault. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `mode` | string | `'Off'` | `[Alert, Deny, Off]` | The configuring of intrusion detection. | +| `privateRanges` | array | `[]` | | List of private IP addresses/IP address ranges to not be SNAT. | +| `retentionDays` | int | `365` | | Number of days the insights should be enabled on the policy. | +| `ruleCollectionGroups` | _[ruleCollectionGroups](ruleCollectionGroups/readme.md)_ array | `[]` | | Rule collection groups. | +| `servers` | array | `[]` | | List of Custom DNS Servers. | +| `signatureOverrides` | array | `[]` | | List of specific signatures states. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Firewall policy resource. | +| `threatIntelMode` | string | `'Off'` | `[Alert, Deny, Off]` | The operation mode for Threat Intel. | +| `tier` | string | `'Standard'` | `[Premium, Standard]` | Tier of Firewall Policy. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `workspaces` | array | `[]` | | List of workspaces for Firewall Policy Insights. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed firewall policy. | +| `resourceGroupName` | string | The resource group of the deployed firewall policy. | +| `resourceId` | string | The resource ID of the deployed firewall policy. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module firewallPolicies './Microsoft.Network/firewallPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FirewallPolicies' + params: { + name: '<>-az-fwpol-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fwpol-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module firewallPolicies './Microsoft.Network/firewallPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FirewallPolicies' + params: { + // Required parameters + name: '<>-az-fwpol-x-002' + // Non-required parameters + ruleCollectionGroups: [ + { + name: '<>-rule-001' + priority: 5000 + ruleCollections: [ + { + action: { + type: 'Allow' + } + name: 'collection002' + priority: 5555 + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationFqdns: [] + destinationIpGroups: [] + destinationPorts: [ + '80' + ] + ipProtocols: [ + 'TCP' + 'UDP' + ] + name: 'rule002' + ruleType: 'NetworkRule' + sourceAddresses: [ + '*' + ] + sourceIpGroups: [] + } + ] + } + ] + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-fwpol-x-002" + }, + // Non-required parameters + "ruleCollectionGroups": { + "value": [ + { + "name": "<>-rule-001", + "priority": 5000, + "ruleCollections": [ + { + "action": { + "type": "Allow" + }, + "name": "collection002", + "priority": 5555, + "ruleCollectionType": "FirewallPolicyFilterRuleCollection", + "rules": [ + { + "destinationAddresses": [ + "*" + ], + "destinationFqdns": [], + "destinationIpGroups": [], + "destinationPorts": [ + "80" + ], + "ipProtocols": [ + "TCP", + "UDP" + ], + "name": "rule002", + "ruleType": "NetworkRule", + "sourceAddresses": [ + "*" + ], + "sourceIpGroups": [] + } + ] + } + ] + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep new file mode 100644 index 0000000..ff0a972 --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep @@ -0,0 +1,48 @@ +@description('Conditional. The name of the parent Firewall Policy. Required if the template is used in a standalone deployment.') +param firewallPolicyName string + +@description('Required. The name of the rule collection group to deploy.') +param name string + +@description('Required. Priority of the Firewall Policy Rule Collection Group resource.') +param priority int + +@description('Optional. Group of Firewall Policy rule collections.') +param ruleCollections array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2021-08-01' existing = { + name: firewallPolicyName +} + +resource ruleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2021-08-01' = { + name: name + parent: firewallPolicy + properties: { + priority: priority + ruleCollections: ruleCollections + } +} + +@description('The name of the deployed rule collection group.') +output name string = ruleCollectionGroup.name + +@description('The resource ID of the deployed rule collection group.') +output resourceId string = ruleCollectionGroup.id + +@description('The resource group of the deployed rule collection group.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md new file mode 100644 index 0000000..fdde337 --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md @@ -0,0 +1,48 @@ +# Network Firewall Policies Rule Collection Groups `[Microsoft.Network/firewallPolicies/ruleCollectionGroups]` + +This module deploys Network Firewall Policies Rule Collection Groups. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/firewallPolicies/ruleCollectionGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/firewallPolicies/ruleCollectionGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the rule collection group to deploy. | +| `priority` | int | Priority of the Firewall Policy Rule Collection Group resource. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `firewallPolicyName` | string | The name of the parent Firewall Policy. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ruleCollections` | array | `[]` | Group of Firewall Policy rule collections. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed rule collection group. | +| `resourceGroupName` | string | The resource group of the deployed rule collection group. | +| `resourceId` | string | The resource ID of the deployed rule collection group. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/firewallPolicies/version.json b/modules/Microsoft.Network/firewallPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/firewallPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..cbbe19c --- /dev/null +++ b/modules/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 frontDoor 'Microsoft.Network/frontDoors@2020-05-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(frontDoor.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: frontDoor +}] diff --git a/modules/Microsoft.Network/frontDoors/.test/parameters.json b/modules/Microsoft.Network/frontDoors/.test/parameters.json new file mode 100644 index 0000000..e52cca1 --- /dev/null +++ b/modules/Microsoft.Network/frontDoors/.test/parameters.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fd-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "backendPools": { + "value": [ + { + "name": "backendPool", + "properties": { + "backends": [ + { + "address": "biceptest.local", + "backendHostHeader": "backendAddress", + "httpPort": 80, + "httpsPort": 443, + "weight": 50, + "priority": 1, + "enabledState": "Enabled", + "privateLinkAlias": "", + "privateLinkApprovalMessage": "", + "privateLinkLocation": "", + "privateLinkResourceId": "" + } + ], + "LoadBalancingSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/LoadBalancingSettings/loadBalancer" + }, + "HealthProbeSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/HealthProbeSettings/heathProbe" + } + } + } + ] + }, + "enforceCertificateNameCheck": { + "value": "Disabled" + }, + "sendRecvTimeoutSeconds": { + "value": 10 + }, + "frontendEndpoints": { + "value": [ + { + "name": "frontEnd", + "properties": { + "hostName": "<>-az-fd-x-001.azurefd.net", + "sessionAffinityEnabledState": "Disabled", + "sessionAffinityTtlSeconds": 60 + } + } + ] + }, + "healthProbeSettings": { + "value": [ + { + "name": "heathProbe", + "properties": { + "enabledState": "", + "healthProbeMethod": "", + "intervalInSeconds": 60, + "path": "/", + "protocol": "Https" + } + } + ] + }, + "loadBalancingSettings": { + "value": [ + { + "name": "loadBalancer", + "properties": { + "additionalLatencyMilliseconds": 0, + "sampleSize": 50, + "successfulSamplesRequired": 1 + } + } + ] + }, + "routingRules": { + "value": [ + { + "name": "routingRule", + "properties": { + "acceptedProtocols": [ + "Http", + "Https" + ], + "enabledState": "Enabled", + "frontendEndpoints": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/FrontendEndpoints/frontEnd" + } + ], + "patternsToMatch": [ + "/*" + ], + "routeConfiguration": { + "@odata.type": "#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration", + "forwardingProtocol": "MatchRequest", + "backendPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/BackendPools/backendPool" + } + } + } + } + ] + } + } +} diff --git a/modules/Microsoft.Network/frontDoors/deploy.bicep b/modules/Microsoft.Network/frontDoors/deploy.bicep new file mode 100644 index 0000000..609b3ed --- /dev/null +++ b/modules/Microsoft.Network/frontDoors/deploy.bicep @@ -0,0 +1,180 @@ +@description('Required. The name of the frontDoor.') +@minLength(1) +@maxLength(64) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Backend address pool of the frontdoor resource.') +param backendPools array = [] + +@description('Optional. Enforce certificate name check of the frontdoor resource.') +param enforceCertificateNameCheck string = 'Disabled' + +@description('Optional. Certificate name check time of the frontdoor resource.') +param sendRecvTimeoutSeconds int = 600 + +@description('Optional. State of the frontdoor resource.') +param enabledState string = 'Enabled' + +@description('Optional. Friendly name of the frontdoor resource.') +param friendlyName string = '' + +@description('Optional. Frontend endpoints of the frontdoor resource.') +param frontendEndpoints array = [] + +@description('Optional. Heath probe settings of the frontdoor resource.') +param healthProbeSettings array = [] + +@description('Optional. Load balancing settings of the frontdoor resource.') +param loadBalancingSettings array = [] + +@description('Optional. Routing rules settings of the frontdoor resource.') +param routingRules array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'FrontdoorAccessLog' + 'FrontdoorWebApplicationFirewallLog' +]) +param logsToEnable array = [ + 'FrontdoorAccessLog' + 'FrontdoorWebApplicationFirewallLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param metricsToEnable array = [ + 'AllMetrics' +] + +var diagnosticsLogs = [for log in logsToEnable: { + category: log + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in metricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource frontDoor 'Microsoft.Network/frontDoors@2020-05-01' = { + name: name + location: 'global' + tags: tags + properties: { + backendPools: backendPools + backendPoolsSettings: { + enforceCertificateNameCheck: enforceCertificateNameCheck + sendRecvTimeoutSeconds: sendRecvTimeoutSeconds + } + enabledState: enabledState + friendlyName: friendlyName + frontendEndpoints: frontendEndpoints + healthProbeSettings: healthProbeSettings + loadBalancingSettings: loadBalancingSettings + routingRules: routingRules + } +} + +resource frontDoor_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${frontDoor.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: frontDoor +} + +resource frontDoor_diagnosticSettingName 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: '${frontDoor.name}-diagnosticSettings' + properties: { + storageAccountId: empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId + workspaceId: empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId + eventHubAuthorizationRuleId: empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId + eventHubName: empty(diagnosticEventHubName) ? null : diagnosticEventHubName + metrics: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsMetrics + logs: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsLogs + } + scope: frontDoor +} + +module frontDoor_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: frontDoor.id + } +}] + +@description('The name of the front door.') +output name string = frontDoor.name + +@description('The resource ID of the front door.') +output resourceId string = frontDoor.id + +@description('The resource group the front door was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/frontDoors/readme.md b/modules/Microsoft.Network/frontDoors/readme.md new file mode 100644 index 0000000..05d36d4 --- /dev/null +++ b/modules/Microsoft.Network/frontDoors/readme.md @@ -0,0 +1,410 @@ +# Front Doors `[Microsoft.Network/frontDoors]` + +This module deploys Front Doors. + + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/frontDoors` | [2020-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-05-01/frontDoors) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the frontDoor. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backendPools` | array | `[]` | | Backend address pool of the frontdoor resource. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enabledState` | string | `'Enabled'` | | State of the frontdoor resource. | +| `enforceCertificateNameCheck` | string | `'Disabled'` | | Enforce certificate name check of the frontdoor resource. | +| `friendlyName` | string | `''` | | Friendly name of the frontdoor resource. | +| `frontendEndpoints` | array | `[]` | | Frontend endpoints of the frontdoor resource. | +| `healthProbeSettings` | array | `[]` | | Heath probe settings of the frontdoor resource. | +| `loadBalancingSettings` | array | `[]` | | Load balancing settings of the frontdoor resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `logsToEnable` | array | `[FrontdoorAccessLog, FrontdoorWebApplicationFirewallLog]` | `[FrontdoorAccessLog, FrontdoorWebApplicationFirewallLog]` | The name of logs that will be streamed. | +| `metricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `roleAssignments` | array | `[]` | | 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'. | +| `routingRules` | array | `[]` | | Routing rules settings of the frontdoor resource. | +| `sendRecvTimeoutSeconds` | int | `600` | | Certificate name check time of the frontdoor resource. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the front door. | +| `resourceGroupName` | string | The resource group the front door was deployed into. | +| `resourceId` | string | The resource ID of the front door. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module frontDoors './Microsoft.Network/frontDoors/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-FrontDoors' + params: { + // Required parameters + name: '<>-az-fd-x-001' + // Non-required parameters + backendPools: [ + { + name: 'backendPool' + properties: { + backends: [ + { + address: 'biceptest.local' + backendHostHeader: 'backendAddress' + enabledState: 'Enabled' + httpPort: 80 + httpsPort: 443 + priority: 1 + privateLinkAlias: '' + privateLinkApprovalMessage: '' + privateLinkLocation: '' + privateLinkResourceId: '' + weight: 50 + } + ] + HealthProbeSettings: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/HealthProbeSettings/heathProbe' + } + LoadBalancingSettings: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/LoadBalancingSettings/loadBalancer' + } + } + } + ] + enforceCertificateNameCheck: 'Disabled' + frontendEndpoints: [ + { + name: 'frontEnd' + properties: { + hostName: '<>-az-fd-x-001.azurefd.net' + sessionAffinityEnabledState: 'Disabled' + sessionAffinityTtlSeconds: 60 + } + } + ] + healthProbeSettings: [ + { + name: 'heathProbe' + properties: { + enabledState: '' + healthProbeMethod: '' + intervalInSeconds: 60 + path: '/' + protocol: 'Https' + } + } + ] + loadBalancingSettings: [ + { + name: 'loadBalancer' + properties: { + additionalLatencyMilliseconds: 0 + sampleSize: 50 + successfulSamplesRequired: 1 + } + } + ] + lock: 'CanNotDelete' + routingRules: [ + { + name: 'routingRule' + properties: { + acceptedProtocols: [ + 'Http' + 'Https' + ] + enabledState: 'Enabled' + frontendEndpoints: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/FrontendEndpoints/frontEnd' + } + ] + patternsToMatch: [ + '/*' + ] + routeConfiguration: { + '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration' + backendPool: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/BackendPools/backendPool' + } + forwardingProtocol: 'MatchRequest' + } + } + } + ] + sendRecvTimeoutSeconds: 10 + } +} +``` + +
+

+ +

+ +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": "<>-az-fd-x-001" + }, + // Non-required parameters + "backendPools": { + "value": [ + { + "name": "backendPool", + "properties": { + "backends": [ + { + "address": "biceptest.local", + "backendHostHeader": "backendAddress", + "enabledState": "Enabled", + "httpPort": 80, + "httpsPort": 443, + "priority": 1, + "privateLinkAlias": "", + "privateLinkApprovalMessage": "", + "privateLinkLocation": "", + "privateLinkResourceId": "", + "weight": 50 + } + ], + "HealthProbeSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/HealthProbeSettings/heathProbe" + }, + "LoadBalancingSettings": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/LoadBalancingSettings/loadBalancer" + } + } + } + ] + }, + "enforceCertificateNameCheck": { + "value": "Disabled" + }, + "frontendEndpoints": { + "value": [ + { + "name": "frontEnd", + "properties": { + "hostName": "<>-az-fd-x-001.azurefd.net", + "sessionAffinityEnabledState": "Disabled", + "sessionAffinityTtlSeconds": 60 + } + } + ] + }, + "healthProbeSettings": { + "value": [ + { + "name": "heathProbe", + "properties": { + "enabledState": "", + "healthProbeMethod": "", + "intervalInSeconds": 60, + "path": "/", + "protocol": "Https" + } + } + ] + }, + "loadBalancingSettings": { + "value": [ + { + "name": "loadBalancer", + "properties": { + "additionalLatencyMilliseconds": 0, + "sampleSize": 50, + "successfulSamplesRequired": 1 + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "routingRules": { + "value": [ + { + "name": "routingRule", + "properties": { + "acceptedProtocols": [ + "Http", + "Https" + ], + "enabledState": "Enabled", + "frontendEndpoints": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/FrontendEndpoints/frontEnd" + } + ], + "patternsToMatch": [ + "/*" + ], + "routeConfiguration": { + "@odata.type": "#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration", + "backendPool": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/frontDoors/<>-az-fd-x-001/BackendPools/backendPool" + }, + "forwardingProtocol": "MatchRequest" + } + } + } + ] + }, + "sendRecvTimeoutSeconds": { + "value": 10 + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/frontDoors/version.json b/modules/Microsoft.Network/frontDoors/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/frontDoors/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..b1db321 --- /dev/null +++ b/modules/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 ipGroup 'Microsoft.Network/ipGroups@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(ipGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: ipGroup +}] diff --git a/modules/Microsoft.Network/ipGroups/.test/parameters.json b/modules/Microsoft.Network/ipGroups/.test/parameters.json new file mode 100644 index 0000000..b30fd0d --- /dev/null +++ b/modules/Microsoft.Network/ipGroups/.test/parameters.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "iacsGroup-servers" + }, + "lock": { + "value": "CanNotDelete" + }, + "ipAddresses": { + "value": [ + "10.0.0.1", + "10.0.0.2" + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/ipGroups/deploy.bicep b/modules/Microsoft.Network/ipGroups/deploy.bicep new file mode 100644 index 0000000..d81f517 --- /dev/null +++ b/modules/Microsoft.Network/ipGroups/deploy.bicep @@ -0,0 +1,81 @@ +@description('Required. The name of the ipGroups.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. IpAddresses/IpAddressPrefixes in the IpGroups resource.') +param ipAddresses array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource ipGroup 'Microsoft.Network/ipGroups@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + ipAddresses: ipAddresses + } +} + +resource ipGroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${ipGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: ipGroup +} + +module ipGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-IPGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: ipGroup.id + } +}] + +@description('The resource ID of the IP group.') +output resourceId string = ipGroup.id + +@description('The resource group of the IP group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the IP group.') +output name string = ipGroup.name + +@description('The location the resource was deployed into.') +output location string = ipGroup.location diff --git a/modules/Microsoft.Network/ipGroups/readme.md b/modules/Microsoft.Network/ipGroups/readme.md new file mode 100644 index 0000000..799e5c7 --- /dev/null +++ b/modules/Microsoft.Network/ipGroups/readme.md @@ -0,0 +1,230 @@ +# IP Groups `[Microsoft.Network/ipGroups]` + +This module deploys an IP group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/ipGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/ipGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the ipGroups. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ipAddresses` | array | `[]` | | IpAddresses/IpAddressPrefixes in the IpGroups resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the IP group. | +| `resourceGroupName` | string | The resource group of the IP group was deployed into. | +| `resourceId` | string | The resource ID of the IP group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module ipGroups './Microsoft.Network/ipGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-IpGroups' + params: { + // Required parameters + name: 'iacsGroup-servers' + // Non-required parameters + ipAddresses: [ + '10.0.0.1' + '10.0.0.2' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "iacsGroup-servers" + }, + // Non-required parameters + "ipAddresses": { + "value": [ + "10.0.0.1", + "10.0.0.2" + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/ipGroups/version.json b/modules/Microsoft.Network/ipGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/ipGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..875cd22 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,74 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(loadBalancer.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: loadBalancer +}] diff --git a/modules/Microsoft.Network/loadBalancers/.test/internal.parameters.json b/modules/Microsoft.Network/loadBalancers/.test/internal.parameters.json new file mode 100644 index 0000000..4c3ffb0 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/.test/internal.parameters.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-lb-internal-001" + }, + "loadBalancerSku": { + "value": "Standard" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "privateIPConfig1", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "backendAddressPools": { + "value": [ + { + "name": "servers" + } + ] + }, + "probes": { + "value": [ + { + "name": "probe1", + "protocol": "Tcp", + "port": "62000", + "intervalInSeconds": 5, + "numberOfProbes": 2 + } + ] + }, + "loadBalancingRules": { + "value": [ + { + "name": "privateIPLBRule1", + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 0, + "backendPort": 0, + "enableFloatingIP": true, + "idleTimeoutInMinutes": 4, + "protocol": "All", + "loadDistribution": "Default", + "probeName": "probe1", + "disableOutboundSnat": true, + "enableTcpReset": false, + "backendAddressPoolName": "servers" + } + ] + }, + "inboundNatRules": { + "value": [ + { + "name": "inboundNatRule1", + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 443, + "backendPort": 443, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 4, + "protocol": "Tcp", + "enableTcpReset": false + }, + { + "name": "inboundNatRule2", + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 3389, + "backendPort": 3389 + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/loadBalancers/.test/min.parameters.json b/modules/Microsoft.Network/loadBalancers/.test/min.parameters.json new file mode 100644 index 0000000..695027d --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/.test/min.parameters.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-lb-min-001" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-min-lb" + } + ] + } + } +} diff --git a/modules/Microsoft.Network/loadBalancers/.test/parameters.json b/modules/Microsoft.Network/loadBalancers/.test/parameters.json new file mode 100644 index 0000000..df44d93 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/.test/parameters.json @@ -0,0 +1,129 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-lb-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-lb" + } + ] + }, + "backendAddressPools": { + "value": [ + { + "name": "backendAddressPool1" + }, + { + "name": "backendAddressPool2" + } + ] + }, + "loadBalancingRules": { + "value": [ + { + "name": "publicIPLBRule1", + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 80, + "backendPort": 80, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "protocol": "Tcp", + "enableTcpReset": false, + "loadDistribution": "Default", + "disableOutboundSnat": true, + "probeName": "probe1", + "backendAddressPoolName": "backendAddressPool1" + }, + { + "name": "publicIPLBRule2", + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 8080, + "backendPort": 8080, + "loadDistribution": "Default", + "probeName": "probe2", + "backendAddressPoolName": "backendAddressPool2" + } + ] + }, + "inboundNatRules": { + "value": [ + { + "name": "inboundNatRule1", + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 443, + "backendPort": 443, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 4, + "protocol": "Tcp", + "enableTcpReset": false + }, + { + "name": "inboundNatRule2", + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 3389, + "backendPort": 3389 + } + ] + }, + "outboundRules": { + "value": [ + { + "name": "outboundRule1", + "frontendIPConfigurationName": "publicIPConfig1", + "backendAddressPoolName": "backendAddressPool1", + "allocatedOutboundPorts": 63984 + } + ] + }, + "probes": { + "value": [ + { + "name": "probe1", + "protocol": "Tcp", + "port": 80, + "intervalInSeconds": 10, + "numberOfProbes": 5 + }, + { + "name": "probe2", + "protocol": "Https", + "port": 443, + "requestPath": "/" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep b/modules/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep new file mode 100644 index 0000000..83b38d1 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep @@ -0,0 +1,48 @@ +@description('Conditional. The name of the parent load balancer. Required if the template is used in a standalone deployment.') +param loadBalancerName string + +@description('Required. The name of the backend address pool.') +param name string + +@description('Optional. An array of backend addresses.') +param loadBalancerBackendAddresses array = [] + +@description('Optional. An array of gateway load balancer tunnel interfaces.') +param tunnelInterfaces array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = { + name: loadBalancerName +} + +resource backendAddressPool 'Microsoft.Network/loadBalancers/backendAddressPools@2021-08-01' = { + name: name + properties: { + loadBalancerBackendAddresses: loadBalancerBackendAddresses + tunnelInterfaces: tunnelInterfaces + } + parent: loadBalancer +} + +@description('The name of the backend address pool.') +output name string = backendAddressPool.name + +@description('The resource ID of the backend address pool.') +output resourceId string = backendAddressPool.id + +@description('The resource group the backend address pool was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/loadBalancers/backendAddressPools/readme.md b/modules/Microsoft.Network/loadBalancers/backendAddressPools/readme.md new file mode 100644 index 0000000..feebf33 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/backendAddressPools/readme.md @@ -0,0 +1,48 @@ +# Load Balancers Backend Address Pools `[Microsoft.Network/loadBalancers/backendAddressPools]` + +This module deploys load balancer backend address pools. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/loadBalancers/backendAddressPools` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/loadBalancers/backendAddressPools) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backend address pool. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `loadBalancerName` | string | The name of the parent load balancer. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `loadBalancerBackendAddresses` | array | `[]` | An array of backend addresses. | +| `tunnelInterfaces` | array | `[]` | An array of gateway load balancer tunnel interfaces. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backend address pool. | +| `resourceGroupName` | string | The resource group the backend address pool was deployed into. | +| `resourceId` | string | The resource ID of the backend address pool. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/loadBalancers/backendAddressPools/version.json b/modules/Microsoft.Network/loadBalancers/backendAddressPools/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/backendAddressPools/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/loadBalancers/deploy.bicep b/modules/Microsoft.Network/loadBalancers/deploy.bicep new file mode 100644 index 0000000..33757cd --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/deploy.bicep @@ -0,0 +1,275 @@ +@description('Required. The Proximity Placement Groups Name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Name of a load balancer SKU.') +@allowed([ + 'Basic' + 'Standard' +]) +param loadBalancerSku string = 'Standard' + +@description('Required. Array of objects containing all frontend IP configurations.') +@minLength(1) +param frontendIPConfigurations array + +@description('Optional. Collection of backend address pools used by a load balancer.') +param backendAddressPools array = [] + +@description('Optional. Array of objects containing all load balancing rules.') +param loadBalancingRules array = [] + +@description('Optional. Array of objects containing all probes, these are references in the load balancing rules.') +param probes array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Collection of inbound NAT Rules used by a load balancer. Defining inbound NAT rules on your load balancer is mutually exclusive with defining an inbound NAT pool. Inbound NAT pools are referenced from virtual machine scale sets. NICs that are associated with individual virtual machines cannot reference an Inbound NAT pool. They have to reference individual inbound NAT rules.') +param inboundNatRules array = [] + +@description('Optional. The outbound rules.') +param outboundRules array = [] + +var frontendIPConfigurations_var = [for (frontendIPConfiguration, index) in frontendIPConfigurations: { + name: frontendIPConfiguration.name + properties: { + subnet: contains(frontendIPConfiguration, 'subnetId') && !empty(frontendIPConfiguration.subnetId) ? { + id: frontendIPConfiguration.subnetId + } : null + publicIPAddress: contains(frontendIPConfiguration, 'publicIPAddressId') && !empty(frontendIPConfiguration.publicIPAddressId) ? { + id: frontendIPConfiguration.publicIPAddressId + } : null + privateIPAddress: contains(frontendIPConfiguration, 'privateIPAddress') && !empty(frontendIPConfiguration.privateIPAddress) ? frontendIPConfiguration.privateIPAddress : null + privateIPAddressVersion: contains(frontendIPConfiguration, 'privateIPAddressVersion') ? frontendIPConfiguration.privateIPAddressVersion : 'IPv4' + privateIPAllocationMethod: contains(frontendIPConfiguration, 'subnetId') && !empty(frontendIPConfiguration.subnetId) ? (contains(frontendIPConfiguration, 'privateIPAddress') ? 'Static' : 'Dynamic') : null + gatewayLoadBalancer: contains(frontendIPConfiguration, 'gatewayLoadBalancer') && !empty(frontendIPConfiguration.gatewayLoadBalancer) ? { + id: frontendIPConfiguration.gatewayLoadBalancer + } : null + publicIPPrefix: contains(frontendIPConfiguration, 'publicIPPrefix') && !empty(frontendIPConfiguration.publicIPPrefix) ? { + id: frontendIPConfiguration.publicIPPrefix + } : null + } +}] + +var loadBalancingRules_var = [for loadBalancingRule in loadBalancingRules: { + name: loadBalancingRule.name + properties: { + backendAddressPool: { + id: az.resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, loadBalancingRule.backendAddressPoolName) + } + backendPort: loadBalancingRule.backendPort + disableOutboundSnat: contains(loadBalancingRule, 'disableOutboundSnat') ? loadBalancingRule.disableOutboundSnat : true + enableFloatingIP: contains(loadBalancingRule, 'enableFloatingIP') ? loadBalancingRule.enableFloatingIP : false + enableTcpReset: contains(loadBalancingRule, 'enableTcpReset') ? loadBalancingRule.enableTcpReset : false + frontendIPConfiguration: { + id: az.resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, loadBalancingRule.frontendIPConfigurationName) + } + frontendPort: loadBalancingRule.frontendPort + idleTimeoutInMinutes: contains(loadBalancingRule, 'idleTimeoutInMinutes') ? loadBalancingRule.idleTimeoutInMinutes : 4 + loadDistribution: contains(loadBalancingRule, 'loadDistribution') ? loadBalancingRule.loadDistribution : 'Default' + probe: { + id: '${az.resourceId('Microsoft.Network/loadBalancers', name)}/probes/${loadBalancingRule.probeName}' + } + protocol: contains(loadBalancingRule, 'protocol') ? loadBalancingRule.protocol : 'Tcp' + } +}] + +var outboundRules_var = [for outboundRule in outboundRules: { + name: outboundRule.name + properties: { + frontendIPConfigurations: [ + { + id: az.resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, outboundRule.frontendIPConfigurationName) + } + ] + backendAddressPool: { + id: az.resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, outboundRule.backendAddressPoolName) + } + protocol: contains(outboundRule, 'protocol') ? outboundRule.protocol : 'All' + allocatedOutboundPorts: contains(outboundRule, 'allocatedOutboundPorts') ? outboundRule.allocatedOutboundPorts : 63984 + enableTcpReset: contains(outboundRule, 'enableTcpReset') ? outboundRule.enableTcpReset : true + idleTimeoutInMinutes: contains(outboundRule, 'idleTimeoutInMinutes') ? outboundRule.idleTimeoutInMinutes : 4 + } +}] + +var probes_var = [for probe in probes: { + name: probe.name + properties: { + protocol: contains(probe, 'protocol') ? probe.protocol : 'Tcp' + requestPath: toLower(probe.protocol) != 'tcp' ? probe.requestPath : null + port: contains(probe, 'port') ? probe.port : 80 + intervalInSeconds: contains(probe, 'intervalInSeconds') ? probe.intervalInSeconds : 5 + numberOfProbes: contains(probe, 'numberOfProbes') ? probe.numberOfProbes : 2 + } +}] + +var backendAddressPoolNames = [for backendAddressPool in backendAddressPools: { + name: backendAddressPool.name +}] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: loadBalancerSku + } + properties: { + frontendIPConfigurations: frontendIPConfigurations_var + loadBalancingRules: loadBalancingRules_var + backendAddressPools: backendAddressPoolNames + outboundRules: outboundRules_var + probes: probes_var + } +} + +module loadBalancer_backendAddressPools 'backendAddressPools/deploy.bicep' = [for (backendAddressPool, index) in backendAddressPools: { + name: '${uniqueString(deployment().name, location)}-loadBalancer-backendAddressPools-${index}' + params: { + loadBalancerName: loadBalancer.name + name: backendAddressPool.name + tunnelInterfaces: contains(backendAddressPool, 'tunnelInterfaces') && !empty(backendAddressPool.tunnelInterfaces) ? backendAddressPool.tunnelInterfaces : [] + loadBalancerBackendAddresses: contains(backendAddressPool, 'loadBalancerBackendAddresses') && !empty(backendAddressPool.loadBalancerBackendAddresses) ? backendAddressPool.loadBalancerBackendAddresses : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module loadBalancer_inboundNATRules 'inboundNatRules/deploy.bicep' = [for (inboundNATRule, index) in inboundNatRules: { + name: '${uniqueString(deployment().name, location)}-LoadBalancer-inboundNatRules-${index}' + params: { + loadBalancerName: loadBalancer.name + name: inboundNATRule.name + frontendIPConfigurationName: inboundNATRule.frontendIPConfigurationName + frontendPort: inboundNATRule.frontendPort + backendPort: contains(inboundNATRule, 'backendPort') ? inboundNATRule.backendPort : inboundNATRule.frontendPort + backendAddressPoolName: contains(inboundNATRule, 'backendAddressPoolName') ? inboundNATRule.backendAddressPoolName : '' + enableFloatingIP: contains(inboundNATRule, 'enableFloatingIP') ? inboundNATRule.enableFloatingIP : false + enableTcpReset: contains(inboundNATRule, 'enableTcpReset') ? inboundNATRule.enableTcpReset : false + frontendPortRangeEnd: contains(inboundNATRule, 'frontendPortRangeEnd') ? inboundNATRule.frontendPortRangeEnd : -1 + frontendPortRangeStart: contains(inboundNATRule, 'frontendPortRangeStart') ? inboundNATRule.frontendPortRangeStart : -1 + idleTimeoutInMinutes: contains(inboundNATRule, 'idleTimeoutInMinutes') ? inboundNATRule.idleTimeoutInMinutes : 4 + protocol: contains(inboundNATRule, 'protocol') ? inboundNATRule.protocol : 'Tcp' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + loadBalancer_backendAddressPools + ] +}] + +resource loadBalancer_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${loadBalancer.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: loadBalancer +} + +resource loadBalancer_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: loadBalancer +} + +module loadBalancer_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LoadBalancer-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: loadBalancer.id + } +}] + +@description('The name of the load balancer.') +output name string = loadBalancer.name + +@description('The resource ID of the load balancer.') +output resourceId string = loadBalancer.id + +@description('The resource group the load balancer was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The backend address pools available in the load balancer.') +output backendpools array = loadBalancer.properties.backendAddressPools + +@description('The location the resource was deployed into.') +output location string = loadBalancer.location diff --git a/modules/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep b/modules/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep new file mode 100644 index 0000000..b9ee9ec --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep @@ -0,0 +1,97 @@ +@description('Conditional. The name of the parent load balancer. Required if the template is used in a standalone deployment.') +param loadBalancerName string + +@description('Required. The name of the inbound NAT rule.') +param name string + +@description('Required. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer.') +@minValue(1) +@maxValue(65534) +param frontendPort int + +@description('Optional. The port used for the internal endpoint.') +@minValue(1) +@maxValue(65535) +param backendPort int = frontendPort + +@description('Optional. Name of the backend address pool.') +param backendAddressPoolName string = '' + +@description('Optional. Configures a virtual machine\'s endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can\'t be changed after you create the endpoint.') +param enableFloatingIP bool = false + +@description('Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP.') +param enableTcpReset bool = false + +@description('Required. The name of the frontend IP address to set for the inbound NAT rule.') +param frontendIPConfigurationName string + +@description('Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool.') +@minValue(-1) +@maxValue(65534) +param frontendPortRangeEnd int = -1 + +@description('Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool.') +@minValue(-1) +@maxValue(65534) +param frontendPortRangeStart int = -1 + +@description('Optional. The timeout for the TCP idle connection. The value can be set between 4 and 30 minutes. The default value is 4 minutes. This element is only used when the protocol is set to TCP.') +param idleTimeoutInMinutes int = 4 + +@description('Optional. The transport protocol for the endpoint.') +@allowed([ + 'All' + 'Tcp' + 'Udp' +]) +param protocol string = 'Tcp' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = { + name: loadBalancerName +} + +resource inboundNatRule 'Microsoft.Network/loadBalancers/inboundNatRules@2021-08-01' = { + name: name + properties: { + frontendPort: frontendPort + backendPort: backendPort + backendAddressPool: !empty(backendAddressPoolName) ? { + id: az.resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, backendAddressPoolName) + } : null + enableFloatingIP: enableFloatingIP + enableTcpReset: enableTcpReset + frontendIPConfiguration: { + id: '${loadBalancer.id}/frontendIPConfigurations/${frontendIPConfigurationName}' + } + frontendPortRangeStart: frontendPortRangeStart != -1 ? frontendPortRangeStart : null + frontendPortRangeEnd: frontendPortRangeEnd != -1 ? frontendPortRangeEnd : null + idleTimeoutInMinutes: idleTimeoutInMinutes + protocol: protocol + } + parent: loadBalancer +} + +@description('The name of the inbound NAT rule.') +output name string = inboundNatRule.name + +@description('The resource ID of the inbound NAT rule.') +output resourceId string = inboundNatRule.id + +@description('The resource group the inbound NAT rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/loadBalancers/inboundNatRules/readme.md b/modules/Microsoft.Network/loadBalancers/inboundNatRules/readme.md new file mode 100644 index 0000000..83bc474 --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/inboundNatRules/readme.md @@ -0,0 +1,56 @@ +# Load Balancer Inbound NAT Rules `[Microsoft.Network/loadBalancers/inboundNatRules]` + +This module deploys load balancers inbound NAT rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/loadBalancers/inboundNatRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/loadBalancers/inboundNatRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `frontendIPConfigurationName` | string | The name of the frontend IP address to set for the inbound NAT rule. | +| `frontendPort` | int | The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. | +| `name` | string | The name of the inbound NAT rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `loadBalancerName` | string | The name of the parent load balancer. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backendAddressPoolName` | string | `''` | | Name of the backend address pool. | +| `backendPort` | int | `[parameters('frontendPort')]` | | The port used for the internal endpoint. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableFloatingIP` | bool | `False` | | Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint. | +| `enableTcpReset` | bool | `False` | | Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP. | +| `frontendPortRangeEnd` | int | `-1` | | The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. | +| `frontendPortRangeStart` | int | `-1` | | The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. | +| `idleTimeoutInMinutes` | int | `4` | | The timeout for the TCP idle connection. The value can be set between 4 and 30 minutes. The default value is 4 minutes. This element is only used when the protocol is set to TCP. | +| `protocol` | string | `'Tcp'` | `[All, Tcp, Udp]` | The transport protocol for the endpoint. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the inbound NAT rule. | +| `resourceGroupName` | string | The resource group the inbound NAT rule was deployed into. | +| `resourceId` | string | The resource ID of the inbound NAT rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/loadBalancers/inboundNatRules/version.json b/modules/Microsoft.Network/loadBalancers/inboundNatRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/inboundNatRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/loadBalancers/readme.md b/modules/Microsoft.Network/loadBalancers/readme.md new file mode 100644 index 0000000..1556a0c --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/readme.md @@ -0,0 +1,979 @@ +# Load Balancers `[Microsoft.Network/loadBalancers]` + +This module deploys a load balancer. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/loadBalancers` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/loadBalancers) | +| `Microsoft.Network/loadBalancers/backendAddressPools` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/loadBalancers/backendAddressPools) | +| `Microsoft.Network/loadBalancers/inboundNatRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/loadBalancers/inboundNatRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `frontendIPConfigurations` | array | Array of objects containing all frontend IP configurations. | +| `name` | string | The Proximity Placement Groups Name. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backendAddressPools` | _[backendAddressPools](backendAddressPools/readme.md)_ array | `[]` | | Collection of backend address pools used by a load balancer. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `inboundNatRules` | _[inboundNatRules](inboundNatRules/readme.md)_ array | `[]` | | Collection of inbound NAT Rules used by a load balancer. Defining inbound NAT rules on your load balancer is mutually exclusive with defining an inbound NAT pool. Inbound NAT pools are referenced from virtual machine scale sets. NICs that are associated with individual virtual machines cannot reference an Inbound NAT pool. They have to reference individual inbound NAT rules. | +| `loadBalancerSku` | string | `'Standard'` | `[Basic, Standard]` | Name of a load balancer SKU. | +| `loadBalancingRules` | array | `[]` | | Array of objects containing all load balancing rules. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `outboundRules` | array | `[]` | | The outbound rules. | +| `probes` | array | `[]` | | Array of objects containing all probes, these are references in the load balancing rules. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `frontendIPConfigurations` + +

+ +Parameter JSON format + +```json +"frontendIPConfigurations": { + "value": [ + { + "name": "p_hub-bfw-server-feip", + "properties": { + "publicIPAddressId": "[reference(variables('deploymentPIP-VPN')).outputs.publicIPAddressResourceId.value]", + "subnetId": "", + "privateIPAddress": "" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +frontendIPConfigurations: [ + { + name: 'p_hub-bfw-server-feip' + properties: { + publicIPAddressId: '[reference(variables('deploymentPIP-VPN')).outputs.publicIPAddressResourceId.value]' + subnetId: '' + privateIPAddress: '' + } + } +] +``` + +
+

+ +### Parameter Usage: `backendAddressPools` + +

+ +Parameter JSON format + +```json +"backendAddressPools": { + "value": [ + { + "name": "p_hub-bfw-server-bepool", + "properties": { + "loadBalancerBackendAddresses": [ + { + "name": "iacs-sh-main-pd-01-euw-rg-network_awefwa01p-nic-int-01ipconfig-internal", + "properties": { + "virtualNetwork": { + "id": "[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]" + }, + "ipAddress": "172.22.232.5" + } + }, + { + "name": "iacs-sh-main-pd-01-euw-rg-network_awefwa01p-ha-nic-int-01ipconfig-internal", + "properties": { + "virtualNetwork": { + "id": "[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]" + }, + "ipAddress": "172.22.232.6" + } + } + ] + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +backendAddressPools: [ + { + name: 'p_hub-bfw-server-bepool' + properties: { + loadBalancerBackendAddresses: [ + { + name: 'iacs-sh-main-pd-01-euw-rg-network_awefwa01p-nic-int-01ipconfig-internal' + properties: { + virtualNetwork: { + id: '[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]' + } + ipAddress: '172.22.232.5' + } + } + { + name: 'iacs-sh-main-pd-01-euw-rg-network_awefwa01p-ha-nic-int-01ipconfig-internal' + properties: { + virtualNetwork: { + id: '[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]' + } + ipAddress: '172.22.232.6' + } + } + ] + } + } +] +``` + +
+

+ +### Parameter Usage: `loadBalancingRules` + +

+ +Parameter JSON format + +```json +"loadBalancingRules": { + "value": [ + { + "name": "p_hub-bfw-server-IPSEC-IKE-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 500, + "backendPort": 500, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-IPSEC-NATT-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 4500, + "backendPort": 4500, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-TINA-UDP-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 691, + "backendPort": 691, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-TINA-TCP-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Tcp", + "frontendPort": 691, + "backendPort": 691, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +loadBalancingRules: [ + { + name: 'p_hub-bfw-server-IPSEC-IKE-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 500 + backendPort: 500 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-IPSEC-NATT-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 4500 + backendPort: 4500 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-TINA-UDP-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 691 + backendPort: 691 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-TINA-TCP-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Tcp' + frontendPort: 691 + backendPort: 691 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } +] +``` + +
+

+ +### Parameter Usage: `probes` + +

+ +Parameter JSON format + +```json +"probes": { + "value": [ + { + "name": "p_hub-bfw-server-tcp-65001-probe", + "properties": { + "protocol": "Tcp", + "port": 65001, + "intervalInSeconds": 5, + "numberOfProbes": 2 + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +probes: [ + { + name: 'p_hub-bfw-server-tcp-65001-probe' + properties: { + protocol: 'Tcp' + port: 65001 + intervalInSeconds: 5 + numberOfProbes: 2 + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `backendpools` | array | The backend address pools available in the load balancer. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the load balancer. | +| `resourceGroupName` | string | The resource group the load balancer was deployed into. | +| `resourceId` | string | The resource ID of the load balancer. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Internal

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-LoadBalancers' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'privateIPConfig1' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + name: '<>-az-lb-internal-001' + // Non-required parameters + backendAddressPools: [ + { + name: 'servers' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + loadBalancerSku: 'Standard' + loadBalancingRules: [ + { + backendAddressPoolName: 'servers' + backendPort: 0 + disableOutboundSnat: true + enableFloatingIP: true + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 0 + idleTimeoutInMinutes: 4 + loadDistribution: 'Default' + name: 'privateIPLBRule1' + probeName: 'probe1' + protocol: 'All' + } + ] + probes: [ + { + intervalInSeconds: 5 + name: 'probe1' + numberOfProbes: 2 + port: '62000' + protocol: 'Tcp' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "frontendIPConfigurations": { + "value": [ + { + "name": "privateIPConfig1", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "name": { + "value": "<>-az-lb-internal-001" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "servers" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "inboundNatRules": { + "value": [ + { + "backendPort": 443, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 443, + "idleTimeoutInMinutes": 4, + "name": "inboundNatRule1", + "protocol": "Tcp" + }, + { + "backendPort": 3389, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 3389, + "name": "inboundNatRule2" + } + ] + }, + "loadBalancerSku": { + "value": "Standard" + }, + "loadBalancingRules": { + "value": [ + { + "backendAddressPoolName": "servers", + "backendPort": 0, + "disableOutboundSnat": true, + "enableFloatingIP": true, + "enableTcpReset": false, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 0, + "idleTimeoutInMinutes": 4, + "loadDistribution": "Default", + "name": "privateIPLBRule1", + "probeName": "probe1", + "protocol": "All" + } + ] + }, + "probes": { + "value": [ + { + "intervalInSeconds": 5, + "name": "probe1", + "numberOfProbes": 2, + "port": "62000", + "protocol": "Tcp" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-LoadBalancers' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-min-lb' + } + ] + name: '<>-az-lb-min-001' + } +} +``` + +
+

+ +

+ +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 + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-min-lb" + } + ] + }, + "name": { + "value": "<>-az-lb-min-001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-LoadBalancers' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-lb' + } + ] + name: '<>-az-lb-x-001' + // Non-required parameters + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + { + name: 'backendAddressPool2' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + loadBalancingRules: [ + { + backendAddressPoolName: 'backendAddressPool1' + backendPort: 80 + disableOutboundSnat: true + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 80 + idleTimeoutInMinutes: 5 + loadDistribution: 'Default' + name: 'publicIPLBRule1' + probeName: 'probe1' + protocol: 'Tcp' + } + { + backendAddressPoolName: 'backendAddressPool2' + backendPort: 8080 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 8080 + loadDistribution: 'Default' + name: 'publicIPLBRule2' + probeName: 'probe2' + } + ] + lock: 'CanNotDelete' + outboundRules: [ + { + allocatedOutboundPorts: 63984 + backendAddressPoolName: 'backendAddressPool1' + frontendIPConfigurationName: 'publicIPConfig1' + name: 'outboundRule1' + } + ] + probes: [ + { + intervalInSeconds: 10 + name: 'probe1' + numberOfProbes: 5 + port: 80 + protocol: 'Tcp' + } + { + name: 'probe2' + port: 443 + protocol: 'Https' + requestPath: '/' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-lb" + } + ] + }, + "name": { + "value": "<>-az-lb-x-001" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "backendAddressPool1" + }, + { + "name": "backendAddressPool2" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "inboundNatRules": { + "value": [ + { + "backendPort": 443, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 443, + "idleTimeoutInMinutes": 4, + "name": "inboundNatRule1", + "protocol": "Tcp" + }, + { + "backendPort": 3389, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 3389, + "name": "inboundNatRule2" + } + ] + }, + "loadBalancingRules": { + "value": [ + { + "backendAddressPoolName": "backendAddressPool1", + "backendPort": 80, + "disableOutboundSnat": true, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 80, + "idleTimeoutInMinutes": 5, + "loadDistribution": "Default", + "name": "publicIPLBRule1", + "probeName": "probe1", + "protocol": "Tcp" + }, + { + "backendAddressPoolName": "backendAddressPool2", + "backendPort": 8080, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 8080, + "loadDistribution": "Default", + "name": "publicIPLBRule2", + "probeName": "probe2" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "outboundRules": { + "value": [ + { + "allocatedOutboundPorts": 63984, + "backendAddressPoolName": "backendAddressPool1", + "frontendIPConfigurationName": "publicIPConfig1", + "name": "outboundRule1" + } + ] + }, + "probes": { + "value": [ + { + "intervalInSeconds": 10, + "name": "probe1", + "numberOfProbes": 5, + "port": 80, + "protocol": "Tcp" + }, + { + "name": "probe2", + "port": 443, + "protocol": "Https", + "requestPath": "/" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/loadBalancers/version.json b/modules/Microsoft.Network/loadBalancers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/loadBalancers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..895a5cf --- /dev/null +++ b/modules/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 localNetworkGateway 'Microsoft.Network/localNetworkGateways@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(localNetworkGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: localNetworkGateway +}] diff --git a/modules/Microsoft.Network/localNetworkGateways/.test/parameters.json b/modules/Microsoft.Network/localNetworkGateways/.test/parameters.json new file mode 100644 index 0000000..f2d289d --- /dev/null +++ b/modules/Microsoft.Network/localNetworkGateways/.test/parameters.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-lng-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "localAddressPrefixes": { + "value": [ + "192.168.1.0/24" + ] + }, + "localGatewayPublicIpAddress": { + "value": "8.8.8.8" + }, + "localAsn": { + "value": "65123" + }, + "localBgpPeeringAddress": { + "value": "192.168.1.5" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/localNetworkGateways/deploy.bicep b/modules/Microsoft.Network/localNetworkGateways/deploy.bicep new file mode 100644 index 0000000..2369d03 --- /dev/null +++ b/modules/Microsoft.Network/localNetworkGateways/deploy.bicep @@ -0,0 +1,107 @@ +@description('Required. Name of the Local Network Gateway.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. List of the local (on-premises) IP address ranges.') +param localAddressPrefixes array + +@description('Required. Public IP of the local gateway.') +param localGatewayPublicIpAddress string + +@description('Optional. The BGP speaker\'s ASN. Not providing this value will automatically disable BGP on this Local Network Gateway resource.') +param localAsn string = '' + +@description('Optional. The BGP peering address and BGP identifier of this BGP speaker. Not providing this value will automatically disable BGP on this Local Network Gateway resource.') +param localBgpPeeringAddress string = '' + +@description('Optional. The weight added to routes learned from this BGP speaker. This will only take effect if both the localAsn and the localBgpPeeringAddress values are provided.') +param localPeerWeight string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. FQDN of local network gateway.') +param fqdn string = '' + +var bgpSettings = { + asn: localAsn + bgpPeeringAddress: localBgpPeeringAddress + peerWeight: !empty(localPeerWeight) ? localPeerWeight : '0' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + localNetworkAddressSpace: { + addressPrefixes: localAddressPrefixes + } + fqdn: !empty(fqdn) ? fqdn : null + gatewayIpAddress: localGatewayPublicIpAddress + bgpSettings: !empty(localAsn) && !empty(localBgpPeeringAddress) ? bgpSettings : null + } +} + +resource localNetworkGateway_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${localNetworkGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: localNetworkGateway +} + +module localNetworkGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LocalNetworkGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: localNetworkGateway.id + } +}] + +@description('The resource ID of the local network gateway.') +output resourceId string = localNetworkGateway.id + +@description('The resource group the local network gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the local network gateway.') +output name string = localNetworkGateway.name + +@description('The location the resource was deployed into.') +output location string = localNetworkGateway.location diff --git a/modules/Microsoft.Network/localNetworkGateways/readme.md b/modules/Microsoft.Network/localNetworkGateways/readme.md new file mode 100644 index 0000000..6a078ec --- /dev/null +++ b/modules/Microsoft.Network/localNetworkGateways/readme.md @@ -0,0 +1,245 @@ +# Local Network Gateways `[Microsoft.Network/localNetworkGateways]` + +This module deploys a local network gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/localNetworkGateways` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/localNetworkGateways) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `localAddressPrefixes` | array | List of the local (on-premises) IP address ranges. | +| `localGatewayPublicIpAddress` | string | Public IP of the local gateway. | +| `name` | string | Name of the Local Network Gateway. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `fqdn` | string | `''` | | FQDN of local network gateway. | +| `localAsn` | string | `''` | | The BGP speaker's ASN. Not providing this value will automatically disable BGP on this Local Network Gateway resource. | +| `localBgpPeeringAddress` | string | `''` | | The BGP peering address and BGP identifier of this BGP speaker. Not providing this value will automatically disable BGP on this Local Network Gateway resource. | +| `localPeerWeight` | string | `''` | | The weight added to routes learned from this BGP speaker. This will only take effect if both the localAsn and the localBgpPeeringAddress values are provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the local network gateway. | +| `resourceGroupName` | string | The resource group the local network gateway was deployed into. | +| `resourceId` | string | The resource ID of the local network gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module localNetworkGateways './Microsoft.Network/localNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-LocalNetworkGateways' + params: { + // Required parameters + localAddressPrefixes: [ + '192.168.1.0/24' + ] + localGatewayPublicIpAddress: '8.8.8.8' + name: '<>-az-lng-x-001' + // Non-required parameters + localAsn: '65123' + localBgpPeeringAddress: '192.168.1.5' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "localAddressPrefixes": { + "value": [ + "192.168.1.0/24" + ] + }, + "localGatewayPublicIpAddress": { + "value": "8.8.8.8" + }, + "name": { + "value": "<>-az-lng-x-001" + }, + // Non-required parameters + "localAsn": { + "value": "65123" + }, + "localBgpPeeringAddress": { + "value": "192.168.1.5" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/localNetworkGateways/version.json b/modules/Microsoft.Network/localNetworkGateways/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/localNetworkGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..56304b1 --- /dev/null +++ b/modules/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 natGateway 'Microsoft.Network/natGateways@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(natGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: natGateway +}] diff --git a/modules/Microsoft.Network/natGateways/.test/parameters.json b/modules/Microsoft.Network/natGateways/.test/parameters.json new file mode 100644 index 0000000..ec9c201 --- /dev/null +++ b/modules/Microsoft.Network/natGateways/.test/parameters.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ngw-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "natGatewayPublicIpAddress": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/natGateways/deploy.bicep b/modules/Microsoft.Network/natGateways/deploy.bicep new file mode 100644 index 0000000..0d602a2 --- /dev/null +++ b/modules/Microsoft.Network/natGateways/deploy.bicep @@ -0,0 +1,219 @@ +@description('Required. Name of the Azure Bastion resource.') +param name string + +@description('Optional. The idle timeout of the nat gateway.') +param idleTimeoutInMinutes int = 5 + +@description('Optional. Use to have a new Public IP Address created for the NAT Gateway.') +param natGatewayPublicIpAddress bool = false + +@description('Optional. Specifies the name of the Public IP used by the NAT Gateway. If it\'s not provided, a \'-pip\' suffix will be appended to the Bastion\'s name.') +param natGatewayPipName string = '' + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param natGatewayPublicIPPrefixId string = '' + +@description('Optional. DNS name of the Public IP resource. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com.') +param natGatewayDomainNameLabel string = '' + +@description('Optional. Existing Public IP Address resource names to use for the NAT Gateway.') +param publicIpAddresses array = [] + +@description('Optional. Existing Public IP Prefixes resource names to use for the NAT Gateway.') +param publicIpPrefixes array = [] + +@description('Optional. A list of availability zones denoting the zone in which Nat Gateway should be deployed.') +param zones array = [] + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags for the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param diagnosticLogCategoriesToEnable array = [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var natGatewayPipName_var = (empty(natGatewayPipName) ? '${name}-pip' : natGatewayPipName) +var natGatewayPublicIPPrefix = { + id: natGatewayPublicIPPrefixId +} + +var natGatewayPropertyPublicIPPrefixes = [for publicIpPrefix in publicIpPrefixes: { + id: az.resourceId('Microsoft.Network/publicIPPrefixes', publicIpPrefix) +}] +var natGatewayPropertyPublicIPAddresses = [for publicIpAddress in publicIpAddresses: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', publicIpAddress) +}] +var natGatewayProperties = { + idleTimeoutInMinutes: idleTimeoutInMinutes + publicIpPrefixes: natGatewayPropertyPublicIPPrefixes + publicIpAddresses: natGatewayPropertyPublicIPAddresses +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// PUBLIC IP +// ========= +resource publicIP 'Microsoft.Network/publicIPAddresses@2021-08-01' = if (natGatewayPublicIpAddress) { + name: natGatewayPipName_var + location: location + tags: tags + sku: { + name: 'Standard' + } + properties: { + publicIPAllocationMethod: 'Static' + publicIPPrefix: !empty(natGatewayPublicIPPrefixId) ? natGatewayPublicIPPrefix : null + dnsSettings: !empty(natGatewayDomainNameLabel) ? json('{"domainNameLabel": "${natGatewayDomainNameLabel}"}') : null + } +} + +resource publicIP_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${publicIP.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: publicIP +} + +resource publicIP_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: publicIP +} + +// NAT GATEWAY +// =========== +resource natGateway 'Microsoft.Network/natGateways@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: 'Standard' + } + properties: natGatewayProperties + zones: zones +} + +resource natGateway_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${natGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: natGateway +} + +module natGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NatGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: natGateway.id + } +}] + +@description('The name of the NAT Gateway.') +output name string = natGateway.name + +@description('The resource ID of the NAT Gateway.') +output resourceId string = natGateway.id + +@description('The resource group the NAT Gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = natGateway.location diff --git a/modules/Microsoft.Network/natGateways/readme.md b/modules/Microsoft.Network/natGateways/readme.md new file mode 100644 index 0000000..c91215d --- /dev/null +++ b/modules/Microsoft.Network/natGateways/readme.md @@ -0,0 +1,261 @@ +# NAT Gateways `[Microsoft.Network/natGateways]` + +This module deploys a NAT gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/natGateways` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/natGateways) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Bastion resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `idleTimeoutInMinutes` | int | `5` | | The idle timeout of the nat gateway. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natGatewayDomainNameLabel` | string | `''` | | DNS name of the Public IP resource. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com. | +| `natGatewayPipName` | string | `''` | | Specifies the name of the Public IP used by the NAT Gateway. If it's not provided, a '-pip' suffix will be appended to the Bastion's name. | +| `natGatewayPublicIpAddress` | bool | `False` | | Use to have a new Public IP Address created for the NAT Gateway. | +| `natGatewayPublicIPPrefixId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `publicIpAddresses` | array | `[]` | | Existing Public IP Address resource names to use for the NAT Gateway. | +| `publicIpPrefixes` | array | `[]` | | Existing Public IP Prefixes resource names to use for the NAT Gateway. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags for the resource. | +| `zones` | array | `[]` | | A list of availability zones denoting the zone in which Nat Gateway should be deployed. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the NAT Gateway. | +| `resourceGroupName` | string | The resource group the NAT Gateway was deployed into. | +| `resourceId` | string | The resource ID of the NAT Gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module natGateways './Microsoft.Network/natGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NatGateways' + params: { + // Required parameters + name: '<>-az-ngw-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + natGatewayPublicIpAddress: true + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-ngw-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "natGatewayPublicIpAddress": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/natGateways/version.json b/modules/Microsoft.Network/natGateways/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/natGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..773119e --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,78 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual 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') +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkInterface.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkInterface +}] diff --git a/modules/Microsoft.Network/networkInterfaces/.test/min.parameters.json b/modules/Microsoft.Network/networkInterfaces/.test/min.parameters.json new file mode 100644 index 0000000..070ae28 --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/.test/min.parameters.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-nic-min-001" + }, + "ipConfigurations": { + "value": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + } +} diff --git a/modules/Microsoft.Network/networkInterfaces/.test/parameters.json b/modules/Microsoft.Network/networkInterfaces/.test/parameters.json new file mode 100644 index 0000000..9b22bf2 --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/.test/parameters.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-nic-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "ipConfigurations": { + "value": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/networkInterfaces/deploy.bicep b/modules/Microsoft.Network/networkInterfaces/deploy.bicep new file mode 100644 index 0000000..fe29816 --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/deploy.bicep @@ -0,0 +1,170 @@ +@description('Required. The name of the network interface.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Indicates whether IP forwarding is enabled on this network interface.') +param enableIPForwarding bool = false + +@description('Optional. If the network interface is accelerated networking enabled.') +param enableAcceleratedNetworking bool = false + +@description('Optional. List of DNS servers IP addresses. Use \'AzureProvidedDNS\' to switch to azure provided DNS resolution. \'AzureProvidedDNS\' value cannot be combined with other IPs, it must be the only value in dnsServers collection.') +param dnsServers array = [] + +@description('Optional. The network security group (NSG) to attach to the network interface.') +param networkSecurityGroupResourceId string = '' + +@description('Required. A list of IPConfigurations of the network interface.') +param ipConfigurations array + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource identifier of log analytics.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + enableIPForwarding: enableIPForwarding + enableAcceleratedNetworking: enableAcceleratedNetworking + dnsSettings: !empty(dnsServers) ? { + dnsServers: dnsServers + } : null + networkSecurityGroup: !empty(networkSecurityGroupResourceId) ? { + id: networkSecurityGroupResourceId + } : null + ipConfigurations: [for (ipConfiguration, index) in ipConfigurations: { + name: contains(ipConfiguration, 'name') ? ipConfiguration.name : 'ipconfig0${index + 1}' + properties: { + primary: index == 0 ? true : false + privateIPAllocationMethod: contains(ipConfiguration, 'privateIPAllocationMethod') ? (!empty(ipConfiguration.privateIPAllocationMethod) ? ipConfiguration.privateIPAllocationMethod : null) : null + privateIPAddress: contains(ipConfiguration, 'privateIPAddress') ? (!empty(ipConfiguration.privateIPAddress) ? ipConfiguration.privateIPAddress : null) : null + publicIPAddress: contains(ipConfiguration, 'publicIPAddressResourceId') ? (ipConfiguration.publicIPAddressResourceId != null ? { + id: ipConfiguration.publicIPAddressResourceId + } : null) : null + subnet: { + id: ipConfiguration.subnetResourceId + } + loadBalancerBackendAddressPools: contains(ipConfiguration, 'loadBalancerBackendAddressPools') ? ipConfiguration.loadBalancerBackendAddressPools : null + applicationSecurityGroups: contains(ipConfiguration, 'applicationSecurityGroups') ? ipConfiguration.applicationSecurityGroups : null + applicationGatewayBackendAddressPools: contains(ipConfiguration, 'applicationGatewayBackendAddressPools') ? ipConfiguration.applicationGatewayBackendAddressPools : null + gatewayLoadBalancer: contains(ipConfiguration, 'gatewayLoadBalancer') ? ipConfiguration.gatewayLoadBalancer : null + loadBalancerInboundNatRules: contains(ipConfiguration, 'loadBalancerInboundNatRules') ? ipConfiguration.loadBalancerInboundNatRules : null + privateIPAddressVersion: contains(ipConfiguration, 'privateIPAddressVersion') ? ipConfiguration.privateIPAddressVersion : null + virtualNetworkTaps: contains(ipConfiguration, 'virtualNetworkTaps') ? ipConfiguration.virtualNetworkTaps : null + } + }] + } +} + +resource networkInterface_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: networkInterface +} + +resource networkInterface_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${networkInterface.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkInterface +} + +module networkInterface_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NIC-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkInterface.id + } +}] + +@description('The name of the deployed resource.') +output name string = networkInterface.name + +@description('The resource ID of the deployed resource.') +output resourceId string = networkInterface.id + +@description('The resource group of the deployed resource.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = networkInterface.location diff --git a/modules/Microsoft.Network/networkInterfaces/readme.md b/modules/Microsoft.Network/networkInterfaces/readme.md new file mode 100644 index 0000000..d8c675d --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/readme.md @@ -0,0 +1,376 @@ +# Network Interface `[Microsoft.Network/networkInterfaces]` + +This module deploys Network Interfaces. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/networkInterfaces` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkInterfaces) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `ipConfigurations` | array | A list of IPConfigurations of the network interface. | +| `name` | string | The name of the network interface. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource identifier of log analytics. | +| `dnsServers` | array | `[]` | | List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection. | +| `enableAcceleratedNetworking` | bool | `False` | | If the network interface is accelerated networking enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableIPForwarding` | bool | `False` | | Indicates whether IP forwarding is enabled on this network interface. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkSecurityGroupResourceId` | string | `''` | | The network security group (NSG) to attach to the network interface. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `ipConfigurations` + +The IP configurations to apply to the network interface. + +```json +{ + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] +} +``` + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed resource. | +| `resourceGroupName` | string | The resource group of the deployed resource. | +| `resourceId` | string | The resource ID of the deployed resource. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module networkInterfaces './Microsoft.Network/networkInterfaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkInterfaces' + params: { + // Required parameters + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + name: '<>-az-nic-min-001' + } +} +``` + +
+

+ +

+ +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 + "ipConfigurations": { + "value": [ + { + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "name": { + "value": "<>-az-nic-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module networkInterfaces './Microsoft.Network/networkInterfaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkInterfaces' + params: { + // Required parameters + ipConfigurations: [ + { + applicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + loadBalancerBackendAddressPools: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers' + } + ] + name: 'ipconfig01' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + { + applicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + name: '<>-az-nic-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "ipConfigurations": { + "value": [ + { + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + }, + { + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "name": { + "value": "<>-az-nic-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/networkInterfaces/version.json b/modules/Microsoft.Network/networkInterfaces/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/networkInterfaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ffc1405 --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,73 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkSecurityGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkSecurityGroup +}] diff --git a/modules/Microsoft.Network/networkSecurityGroups/.test/min.parameters.json b/modules/Microsoft.Network/networkSecurityGroups/.test/min.parameters.json new file mode 100644 index 0000000..b079464 --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/.test/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": "<>-az-nsg-min-001" + } + } +} diff --git a/modules/Microsoft.Network/networkSecurityGroups/.test/parameters.json b/modules/Microsoft.Network/networkSecurityGroups/.test/parameters.json new file mode 100644 index 0000000..26cbb1e --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/.test/parameters.json @@ -0,0 +1,103 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-nsg-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "securityRules": { + "value": [ + { + "name": "Specific", + "properties": { + "description": "Tests specific IPs and ports", + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "8080", + "sourceAddressPrefix": "*", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 100, + "direction": "Inbound" + } + }, + { + "name": "Ranges", + "properties": { + "description": "Tests Ranges", + "protocol": "*", + "access": "Allow", + "priority": 101, + "direction": "Inbound", + "sourcePortRanges": [ + "80", + "81" + ], + "destinationPortRanges": [ + "90", + "91" + ], + "sourceAddressPrefixes": [ + "10.0.0.0/16", + "10.1.0.0/16" + ], + "destinationAddressPrefixes": [ + "10.2.0.0/16", + "10.3.0.0/16" + ] + } + }, + { + "name": "Port_8082", + "properties": { + "description": "Allow inbound access on TCP 8082", + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "8082", + "access": "Allow", + "priority": 102, + "direction": "Inbound", + "sourceApplicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "destinationApplicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/networkSecurityGroups/deploy.bicep b/modules/Microsoft.Network/networkSecurityGroups/deploy.bicep new file mode 100644 index 0000000..fd3b5a6 --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/deploy.bicep @@ -0,0 +1,176 @@ +@description('Required. Name of the Network Security Group.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed.') +param securityRules array = [] + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the NSG resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'NetworkSecurityGroupEvent' + 'NetworkSecurityGroupRuleCounter' +]) +param diagnosticLogCategoriesToEnable array = [ + 'NetworkSecurityGroupEvent' + 'NetworkSecurityGroupRuleCounter' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + securityRules: [for securityRule in securityRules: { + name: securityRule.name + properties: { + protocol: securityRule.properties.protocol + access: securityRule.properties.access + priority: securityRule.properties.priority + direction: securityRule.properties.direction + description: contains(securityRule.properties, 'description') ? securityRule.properties.description : '' + sourcePortRange: contains(securityRule.properties, 'sourcePortRange') ? securityRule.properties.sourcePortRange : '' + sourcePortRanges: contains(securityRule.properties, 'sourcePortRanges') ? securityRule.properties.sourcePortRanges : [] + destinationPortRange: contains(securityRule.properties, 'destinationPortRange') ? securityRule.properties.destinationPortRange : '' + destinationPortRanges: contains(securityRule.properties, 'destinationPortRanges') ? securityRule.properties.destinationPortRanges : [] + sourceAddressPrefix: contains(securityRule.properties, 'sourceAddressPrefix') ? securityRule.properties.sourceAddressPrefix : '' + destinationAddressPrefix: contains(securityRule.properties, 'destinationAddressPrefix') ? securityRule.properties.destinationAddressPrefix : '' + sourceAddressPrefixes: contains(securityRule.properties, 'sourceAddressPrefixes') ? securityRule.properties.sourceAddressPrefixes : [] + destinationAddressPrefixes: contains(securityRule.properties, 'destinationAddressPrefixes') ? securityRule.properties.destinationAddressPrefixes : [] + sourceApplicationSecurityGroups: contains(securityRule.properties, 'sourceApplicationSecurityGroups') ? securityRule.properties.sourceApplicationSecurityGroups : [] + destinationApplicationSecurityGroups: contains(securityRule.properties, 'destinationApplicationSecurityGroups') ? securityRule.properties.destinationApplicationSecurityGroups : [] + } + }] + } +} + +module networkSecurityGroup_securityRules 'securityRules/deploy.bicep' = [for (securityRule, index) in securityRules: { + name: '${uniqueString(deployment().name, location)}-securityRule-${index}' + params: { + name: securityRule.name + networkSecurityGroupName: networkSecurityGroup.name + protocol: securityRule.properties.protocol + access: securityRule.properties.access + priority: securityRule.properties.priority + direction: securityRule.properties.direction + description: contains(securityRule.properties, 'description') ? securityRule.properties.description : '' + sourcePortRange: contains(securityRule.properties, 'sourcePortRange') ? securityRule.properties.sourcePortRange : '' + sourcePortRanges: contains(securityRule.properties, 'sourcePortRanges') ? securityRule.properties.sourcePortRanges : [] + destinationPortRange: contains(securityRule.properties, 'destinationPortRange') ? securityRule.properties.destinationPortRange : '' + destinationPortRanges: contains(securityRule.properties, 'destinationPortRanges') ? securityRule.properties.destinationPortRanges : [] + sourceAddressPrefix: contains(securityRule.properties, 'sourceAddressPrefix') ? securityRule.properties.sourceAddressPrefix : '' + destinationAddressPrefix: contains(securityRule.properties, 'destinationAddressPrefix') ? securityRule.properties.destinationAddressPrefix : '' + sourceAddressPrefixes: contains(securityRule.properties, 'sourceAddressPrefixes') ? securityRule.properties.sourceAddressPrefixes : [] + destinationAddressPrefixes: contains(securityRule.properties, 'destinationAddressPrefixes') ? securityRule.properties.destinationAddressPrefixes : [] + sourceApplicationSecurityGroups: contains(securityRule.properties, 'sourceApplicationSecurityGroups') ? securityRule.properties.sourceApplicationSecurityGroups : [] + destinationApplicationSecurityGroups: contains(securityRule.properties, 'destinationApplicationSecurityGroups') ? securityRule.properties.destinationApplicationSecurityGroups : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource networkSecurityGroup_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${networkSecurityGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkSecurityGroup +} + +resource networkSecurityGroup_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: networkSecurityGroup +} + +module networkSecurityGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NSG-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkSecurityGroup.id + } +}] + +@description('The resource group the network security group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the network security group.') +output resourceId string = networkSecurityGroup.id + +@description('The name of the network security group.') +output name string = networkSecurityGroup.name + +@description('The location the resource was deployed into.') +output location string = networkSecurityGroup.location diff --git a/modules/Microsoft.Network/networkSecurityGroups/readme.md b/modules/Microsoft.Network/networkSecurityGroups/readme.md new file mode 100644 index 0000000..fd8c232 --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/readme.md @@ -0,0 +1,416 @@ +# Network Security Groups `[Microsoft.Network/networkSecurityGroups]` + +This template deploys a network security group (NSG) with optional security rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/networkSecurityGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkSecurityGroups) | +| `Microsoft.Network/networkSecurityGroups/securityRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkSecurityGroups/securityRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Network Security Group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[NetworkSecurityGroupEvent, NetworkSecurityGroupRuleCounter]` | `[NetworkSecurityGroupEvent, NetworkSecurityGroupRuleCounter]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `securityRules` | _[securityRules](securityRules/readme.md)_ array | `[]` | | Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed. | +| `tags` | object | `{object}` | | Tags of the NSG resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the network security group. | +| `resourceGroupName` | string | The resource group the network security group was deployed into. | +| `resourceId` | string | The resource ID of the network security group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module networkSecurityGroups './Microsoft.Network/networkSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkSecurityGroups' + params: { + name: '<>-az-nsg-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-nsg-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module networkSecurityGroups './Microsoft.Network/networkSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkSecurityGroups' + params: { + // Required parameters + name: '<>-az-nsg-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + securityRules: [ + { + name: 'Specific' + properties: { + access: 'Allow' + description: 'Tests specific IPs and ports' + destinationAddressPrefix: '*' + destinationPortRange: '8080' + direction: 'Inbound' + priority: 100 + protocol: '*' + sourceAddressPrefix: '*' + sourcePortRange: '*' + } + } + { + name: 'Ranges' + properties: { + access: 'Allow' + description: 'Tests Ranges' + destinationAddressPrefixes: [ + '10.2.0.0/16' + '10.3.0.0/16' + ] + destinationPortRanges: [ + '90' + '91' + ] + direction: 'Inbound' + priority: 101 + protocol: '*' + sourceAddressPrefixes: [ + '10.0.0.0/16' + '10.1.0.0/16' + ] + sourcePortRanges: [ + '80' + '81' + ] + } + } + { + name: 'Port_8082' + properties: { + access: 'Allow' + description: 'Allow inbound access on TCP 8082' + destinationApplicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + destinationPortRange: '8082' + direction: 'Inbound' + priority: 102 + protocol: '*' + sourceApplicationSecurityGroups: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001' + } + ] + sourcePortRange: '*' + } + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-nsg-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "securityRules": { + "value": [ + { + "name": "Specific", + "properties": { + "access": "Allow", + "description": "Tests specific IPs and ports", + "destinationAddressPrefix": "*", + "destinationPortRange": "8080", + "direction": "Inbound", + "priority": 100, + "protocol": "*", + "sourceAddressPrefix": "*", + "sourcePortRange": "*" + } + }, + { + "name": "Ranges", + "properties": { + "access": "Allow", + "description": "Tests Ranges", + "destinationAddressPrefixes": [ + "10.2.0.0/16", + "10.3.0.0/16" + ], + "destinationPortRanges": [ + "90", + "91" + ], + "direction": "Inbound", + "priority": 101, + "protocol": "*", + "sourceAddressPrefixes": [ + "10.0.0.0/16", + "10.1.0.0/16" + ], + "sourcePortRanges": [ + "80", + "81" + ] + } + }, + { + "name": "Port_8082", + "properties": { + "access": "Allow", + "description": "Allow inbound access on TCP 8082", + "destinationApplicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "destinationPortRange": "8082", + "direction": "Inbound", + "priority": 102, + "protocol": "*", + "sourceApplicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ], + "sourcePortRange": "*" + } + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep b/modules/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep new file mode 100644 index 0000000..b6bf4de --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep @@ -0,0 +1,117 @@ +@sys.description('Required. The name of the security rule.') +param name string + +@sys.description('Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment.') +param networkSecurityGroupName string + +@sys.description('Optional. Whether network traffic is allowed or denied.') +@allowed([ + 'Allow' + 'Deny' +]) +param access string = 'Deny' + +@sys.description('Optional. A description for this rule.') +@maxLength(140) +param description string = '' + +@sys.description('Optional. The destination address prefix. CIDR or destination IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used.') +param destinationAddressPrefix string = '' + +@sys.description('Optional. The destination address prefixes. CIDR or destination IP ranges.') +param destinationAddressPrefixes array = [] + +@sys.description('Optional. The application security group specified as destination.') +param destinationApplicationSecurityGroups array = [] + +@sys.description('Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports.') +param destinationPortRange string = '' + +@sys.description('Optional. The destination port ranges.') +param destinationPortRanges array = [] + +@sys.description('Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic.') +@allowed([ + 'Inbound' + 'Outbound' +]) +param direction string + +@sys.description('Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule.') +param priority int + +@sys.description('Required. Network protocol this rule applies to.') +@allowed([ + '*' + 'Ah' + 'Esp' + 'Icmp' + 'Tcp' + 'Udp' +]) +param protocol string + +@sys.description('Optional. The CIDR or source IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. If this is an ingress rule, specifies where network traffic originates from.') +param sourceAddressPrefix string = '' + +@sys.description('Optional. The CIDR or source IP ranges.') +param sourceAddressPrefixes array = [] + +@sys.description('Optional. The application security group specified as source.') +param sourceApplicationSecurityGroups array = [] + +@sys.description('Optional. The source port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports.') +param sourcePortRange string = '' + +@sys.description('Optional. The source port ranges.') +param sourcePortRanges array = [] + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-08-01' existing = { + name: networkSecurityGroupName +} + +resource securityRule 'Microsoft.Network/networkSecurityGroups/securityRules@2021-08-01' = { + name: name + parent: networkSecurityGroup + properties: { + access: access + description: description + destinationAddressPrefix: destinationAddressPrefix + destinationAddressPrefixes: destinationAddressPrefixes + destinationApplicationSecurityGroups: destinationApplicationSecurityGroups + destinationPortRange: destinationPortRange + destinationPortRanges: destinationPortRanges + direction: direction + priority: priority + protocol: protocol + sourceAddressPrefix: sourceAddressPrefix + sourceAddressPrefixes: sourceAddressPrefixes + sourceApplicationSecurityGroups: sourceApplicationSecurityGroups + sourcePortRange: sourcePortRange + sourcePortRanges: sourcePortRanges + } +} + +@sys.description('The resource group the security rule was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The resource ID of the security rule.') +output resourceId string = securityRule.id + +@sys.description('The name of the security rule.') +output name string = securityRule.name diff --git a/modules/Microsoft.Network/networkSecurityGroups/securityRules/readme.md b/modules/Microsoft.Network/networkSecurityGroups/securityRules/readme.md new file mode 100644 index 0000000..f17dbaa --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/securityRules/readme.md @@ -0,0 +1,61 @@ +# Network Security Groups Security Rules `[Microsoft.Network/networkSecurityGroups/securityRules]` + +This module deploys Network Security Group Security Rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkSecurityGroups/securityRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkSecurityGroups/securityRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `direction` | string | `[Inbound, Outbound]` | The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic. | +| `name` | string | | The name of the security rule. | +| `priority` | int | | The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. | +| `protocol` | string | `[*, Ah, Esp, Icmp, Tcp, Udp]` | Network protocol this rule applies to. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkSecurityGroupName` | string | The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `access` | string | `'Deny'` | `[Allow, Deny]` | Whether network traffic is allowed or denied. | +| `description` | string | `''` | | A description for this rule. | +| `destinationAddressPrefix` | string | `''` | | The destination address prefix. CIDR or destination IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. | +| `destinationAddressPrefixes` | array | `[]` | | The destination address prefixes. CIDR or destination IP ranges. | +| `destinationApplicationSecurityGroups` | array | `[]` | | The application security group specified as destination. | +| `destinationPortRange` | string | `''` | | The destination port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports. | +| `destinationPortRanges` | array | `[]` | | The destination port ranges. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `sourceAddressPrefix` | string | `''` | | The CIDR or source IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. If this is an ingress rule, specifies where network traffic originates from. | +| `sourceAddressPrefixes` | array | `[]` | | The CIDR or source IP ranges. | +| `sourceApplicationSecurityGroups` | array | `[]` | | The application security group specified as source. | +| `sourcePortRange` | string | `''` | | The source port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports. | +| `sourcePortRanges` | array | `[]` | | The source port ranges. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the security rule. | +| `resourceGroupName` | string | The resource group the security rule was deployed into. | +| `resourceId` | string | The resource ID of the security rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/networkSecurityGroups/securityRules/version.json b/modules/Microsoft.Network/networkSecurityGroups/securityRules/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/securityRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/networkSecurityGroups/version.json b/modules/Microsoft.Network/networkSecurityGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/networkSecurityGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..6375bca --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 networkWatcher 'Microsoft.Network/networkWatchers@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkWatcher.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkWatcher +}] diff --git a/modules/Microsoft.Network/networkWatchers/.test/min.parameters.json b/modules/Microsoft.Network/networkWatchers/.test/min.parameters.json new file mode 100644 index 0000000..78acbb0 --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/.test/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": { + "location": { + "value": "northeurope" + } + } +} diff --git a/modules/Microsoft.Network/networkWatchers/.test/parameters.json b/modules/Microsoft.Network/networkWatchers/.test/parameters.json new file mode 100644 index 0000000..78a7e1f --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/.test/parameters.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "adp-<>-az-nw-x-001" + }, + "flowLogs": { + "value": [ + { + "targetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001", + "storageId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "enabled": false + }, + { + "name": "adp-<>-az-nsg-x-apgw-flowlog", + "targetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-apgw", + "storageId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "workspaceResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "formatVersion": 1, + "trafficAnalyticsInterval": 10, + "retentionInDays": 8 + } + ] + }, + "connectionMonitors": { + "value": [ + { + "name": "adp-<>-az-conn-mon-x-001", + "endpoints": [ + { + "name": "<>-az-subnet-x-001(validation-rg)", + "type": "AzureVM", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/virtualMachines/adp-<>-vm-01" + }, + { + "name": "Office Portal", + "type": "ExternalAddress", + "address": "www.office.com" + } + ], + "testConfigurations": [ + { + "name": "HTTP Test", + "testFrequencySec": 30, + "protocol": "Http", + "httpConfiguration": { + "port": 80, + "method": "Get", + "requestHeaders": [], + "validStatusCodeRanges": [ + "200" + ], + "preferHTTPS": false + }, + "successThreshold": { + "checksFailedPercent": 5, + "roundTripTimeMs": 100 + } + } + ], + "testGroups": [ + { + "name": "TestHTTPBing", + "disable": false, + "testConfigurations": [ + "HTTP Test" + ], + "sources": [ + "<>-az-subnet-x-001(validation-rg)" + ], + "destinations": [ + "Office Portal" + ] + } + ], + "workspaceResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep b/modules/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep new file mode 100644 index 0000000..994d54d --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep @@ -0,0 +1,76 @@ +@description('Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG.') +param networkWatcherName string = 'NetworkWatcher_${resourceGroup().location}' + +@description('Required. Name of the resource.') +param name string + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. List of connection monitor endpoints.') +param endpoints array = [] + +@description('Optional. List of connection monitor test configurations.') +param testConfigurations array = [] + +@description('Optional. List of connection monitor test groups.') +param testGroups array = [] + +@description('Optional. Specify the Log Analytics Workspace Resource ID.') +param workspaceResourceId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var outputs = !empty(workspaceResourceId) ? [ + { + type: 'Workspace' + workspaceSettings: { + workspaceResourceId: workspaceResourceId + } + } +] : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2021-08-01' existing = { + name: networkWatcherName +} + +resource connectionMonitor 'Microsoft.Network/networkWatchers/connectionMonitors@2021-08-01' = { + name: name + parent: networkWatcher + tags: tags + location: location + properties: { + endpoints: endpoints + testConfigurations: testConfigurations + testGroups: testGroups + outputs: outputs + } +} + +@description('The name of the deployed connection monitor.') +output name string = connectionMonitor.name + +@description('The resource ID of the deployed connection monitor.') +output resourceId string = connectionMonitor.id + +@description('The resource group the connection monitor was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = connectionMonitor.location diff --git a/modules/Microsoft.Network/networkWatchers/connectionMonitors/readme.md b/modules/Microsoft.Network/networkWatchers/connectionMonitors/readme.md new file mode 100644 index 0000000..fe97daa --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/connectionMonitors/readme.md @@ -0,0 +1,90 @@ +# Network Watchers Connection Monitors `[Microsoft.Network/networkWatchers/connectionMonitors]` + +This template deploys Connection Monitors. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkWatchers/connectionMonitors) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `endpoints` | array | `[]` | List of connection monitor endpoints. | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `networkWatcherName` | string | `[format('NetworkWatcher_{0}', resourceGroup().location)]` | Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. | +| `tags` | object | `{object}` | Tags of the resource. | +| `testConfigurations` | array | `[]` | List of connection monitor test configurations. | +| `testGroups` | array | `[]` | List of connection monitor test groups. | +| `workspaceResourceId` | string | `''` | Specify the Log Analytics Workspace Resource ID. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed connection monitor. | +| `resourceGroupName` | string | The resource group the connection monitor was deployed into. | +| `resourceId` | string | The resource ID of the deployed connection monitor. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/networkWatchers/connectionMonitors/version.json b/modules/Microsoft.Network/networkWatchers/connectionMonitors/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/connectionMonitors/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/networkWatchers/deploy.bicep b/modules/Microsoft.Network/networkWatchers/deploy.bicep new file mode 100644 index 0000000..ae77b9c --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/deploy.bicep @@ -0,0 +1,114 @@ +@description('Optional. Name of the Network Watcher resource (hidden).') +@minLength(1) +param name string = 'NetworkWatcher_${location}' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array that contains the Connection Monitors.') +param connectionMonitors array = [] + +@description('Optional. Array that contains the Flow Logs.') +param flowLogs array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2021-08-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource networkWatcher_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${networkWatcher.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkWatcher +} + +module networkWatcher_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkWatcher.id + } +}] + +module networkWatcher_connectionMonitors 'connectionMonitors/deploy.bicep' = [for (connectionMonitor, index) in connectionMonitors: { + name: '${uniqueString(deployment().name, location)}-NW-ConnectionMonitor-${index}' + params: { + endpoints: contains(connectionMonitor, 'endpoints') ? connectionMonitor.endpoints : [] + name: connectionMonitor.name + networkWatcherName: networkWatcher.name + testConfigurations: contains(connectionMonitor, 'testConfigurations') ? connectionMonitor.testConfigurations : [] + testGroups: contains(connectionMonitor, 'testGroups') ? connectionMonitor.testGroups : [] + workspaceResourceId: contains(connectionMonitor, 'workspaceResourceId') ? connectionMonitor.workspaceResourceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module networkWatcher_flowLogs 'flowLogs/deploy.bicep' = [for (flowLog, index) in flowLogs: { + name: '${uniqueString(deployment().name, location)}-NW-FlowLog-${index}' + params: { + enabled: contains(flowLog, 'enabled') ? flowLog.enabled : true + formatVersion: contains(flowLog, 'formatVersion') ? flowLog.formatVersion : 2 + location: contains(flowLog, 'location') ? flowLog.location : location + name: contains(flowLog, 'name') ? flowLog.name : '${last(split(flowLog.targetResourceId, '/'))}-${split(flowLog.targetResourceId, '/')[4]}-flowlog' + networkWatcherName: networkWatcher.name + retentionInDays: contains(flowLog, 'retentionInDays') ? flowLog.retentionInDays : 365 + storageId: flowLog.storageId + targetResourceId: flowLog.targetResourceId + trafficAnalyticsInterval: contains(flowLog, 'trafficAnalyticsInterval') ? flowLog.trafficAnalyticsInterval : 60 + workspaceResourceId: contains(flowLog, 'workspaceResourceId') ? flowLog.workspaceResourceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed network watcher.') +output name string = networkWatcher.name + +@description('The resource ID of the deployed network watcher.') +output resourceId string = networkWatcher.id + +@description('The resource group the network watcher was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = networkWatcher.location diff --git a/modules/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep b/modules/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep new file mode 100644 index 0000000..9458c76 --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep @@ -0,0 +1,105 @@ +@description('Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG.') +param networkWatcherName string = 'NetworkWatcher_${resourceGroup().location}' + +@description('Optional. Name of the resource.') +param name string = '${last(split(targetResourceId, '/'))}-${split(targetResourceId, '/')[4]}-flowlog' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Resource ID of the NSG that must be enabled for Flow Logs.') +param targetResourceId string + +@description('Required. Resource ID of the diagnostic storage account.') +param storageId string + +@description('Optional. If the flow log should be enabled.') +param enabled bool = true + +@description('Optional. The flow log format version.') +@allowed([ + 1 + 2 +]) +param formatVersion int = 2 + +@description('Optional. Specify the Log Analytics Workspace Resource ID.') +param workspaceResourceId string = '' + +@description('Optional. The interval in minutes which would decide how frequently TA service should do flow analytics.') +@allowed([ + 10 + 60 +]) +param trafficAnalyticsInterval int = 60 + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param retentionInDays int = 365 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var flowAnalyticsConfiguration = !empty(workspaceResourceId) && enabled == true ? { + networkWatcherFlowAnalyticsConfiguration: { + enabled: true + workspaceResourceId: workspaceResourceId + trafficAnalyticsInterval: trafficAnalyticsInterval + } +} : { + networkWatcherFlowAnalyticsConfiguration: { + enabled: false + } +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2021-08-01' existing = { + name: networkWatcherName +} + +resource flowLog 'Microsoft.Network/networkWatchers/flowLogs@2021-08-01' = { + name: name + parent: networkWatcher + tags: tags + location: location + properties: { + targetResourceId: targetResourceId + storageId: storageId + enabled: enabled + retentionPolicy: { + days: retentionInDays + enabled: retentionInDays == 0 ? false : true + } + format: { + type: 'JSON' + version: formatVersion + } + flowAnalyticsConfiguration: flowAnalyticsConfiguration + } +} +@description('The name of the flow log.') +output name string = flowLog.name + +@description('The resource ID of the flow log.') +output resourceId string = flowLog.id + +@description('The resource group the flow log was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = flowLog.location diff --git a/modules/Microsoft.Network/networkWatchers/flowLogs/readme.md b/modules/Microsoft.Network/networkWatchers/flowLogs/readme.md new file mode 100644 index 0000000..54e0173 --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/flowLogs/readme.md @@ -0,0 +1,94 @@ +# NSG Flow Logs `[Microsoft.Network/networkWatchers/flowLogs]` + +This module controls the Network Security Group Flow Logs and analytics settings +**Note: this module must be run on the Resource Group where Network Watcher is deployed** + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/flowLogs` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkWatchers/flowLogs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageId` | string | Resource ID of the diagnostic storage account. | +| `targetResourceId` | string | Resource ID of the NSG that must be enabled for Flow Logs. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enabled` | bool | `True` | | If the flow log should be enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `formatVersion` | int | `2` | `[1, 2]` | The flow log format version. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `name` | string | `[format('{0}-{1}-flowlog', last(split(parameters('targetResourceId'), '/')), split(parameters('targetResourceId'), '/')[4])]` | | Name of the resource. | +| `networkWatcherName` | string | `[format('NetworkWatcher_{0}', resourceGroup().location)]` | | Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. | +| `retentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `trafficAnalyticsInterval` | int | `60` | `[10, 60]` | The interval in minutes which would decide how frequently TA service should do flow analytics. | +| `workspaceResourceId` | string | `''` | | Specify the Log Analytics Workspace Resource ID. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the flow log. | +| `resourceGroupName` | string | The resource group the flow log was deployed into. | +| `resourceId` | string | The resource ID of the flow log. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/networkWatchers/flowLogs/version.json b/modules/Microsoft.Network/networkWatchers/flowLogs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/flowLogs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/networkWatchers/readme.md b/modules/Microsoft.Network/networkWatchers/readme.md new file mode 100644 index 0000000..545438a --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/readme.md @@ -0,0 +1,391 @@ +# Network Watchers `[Microsoft.Network/networkWatchers]` + +- This template deploys a network watcher. +- Network Watcher is a default resource which will get created automatically in every region where a virtual network is present with in the network watcher resource group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/networkWatchers` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkWatchers) | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkWatchers/connectionMonitors) | +| `Microsoft.Network/networkWatchers/flowLogs` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/networkWatchers/flowLogs) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectionMonitors` | _[connectionMonitors](connectionMonitors/readme.md)_ array | `[]` | | Array that contains the Connection Monitors. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `flowLogs` | _[flowLogs](flowLogs/readme.md)_ array | `[]` | | Array that contains the Flow Logs. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `name` | string | `[format('NetworkWatcher_{0}', parameters('location'))]` | | Name of the Network Watcher resource (hidden). | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed network watcher. | +| `resourceGroupName` | string | The resource group the network watcher was deployed into. | +| `resourceId` | string | The resource ID of the deployed network watcher. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module networkWatchers './Microsoft.Network/networkWatchers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkWatchers' + params: { + location: 'northeurope' + } +} +``` + +
+

+ +

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

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module networkWatchers './Microsoft.Network/networkWatchers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-NetworkWatchers' + params: { + connectionMonitors: [ + { + endpoints: [ + { + name: '<>-az-subnet-x-001(validation-rg)' + resourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/virtualMachines/adp-<>-vm-01' + type: 'AzureVM' + } + { + address: 'www.office.com' + name: 'Office Portal' + type: 'ExternalAddress' + } + ] + name: 'adp-<>-az-conn-mon-x-001' + testConfigurations: [ + { + httpConfiguration: { + method: 'Get' + port: 80 + preferHTTPS: false + requestHeaders: [] + validStatusCodeRanges: [ + '200' + ] + } + name: 'HTTP Test' + protocol: 'Http' + successThreshold: { + checksFailedPercent: 5 + roundTripTimeMs: 100 + } + testFrequencySec: 30 + } + ] + testGroups: [ + { + destinations: [ + 'Office Portal' + ] + disable: false + name: 'TestHTTPBing' + sources: [ + '<>-az-subnet-x-001(validation-rg)' + ] + testConfigurations: [ + 'HTTP Test' + ] + } + ] + workspaceResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + } + ] + flowLogs: [ + { + enabled: false + storageId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + targetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001' + } + { + formatVersion: 1 + name: 'adp-<>-az-nsg-x-apgw-flowlog' + retentionInDays: 8 + storageId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + targetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-apgw' + trafficAnalyticsInterval: 10 + workspaceResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + } + ] + name: 'adp-<>-az-nw-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "connectionMonitors": { + "value": [ + { + "endpoints": [ + { + "name": "<>-az-subnet-x-001(validation-rg)", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/virtualMachines/adp-<>-vm-01", + "type": "AzureVM" + }, + { + "address": "www.office.com", + "name": "Office Portal", + "type": "ExternalAddress" + } + ], + "name": "adp-<>-az-conn-mon-x-001", + "testConfigurations": [ + { + "httpConfiguration": { + "method": "Get", + "port": 80, + "preferHTTPS": false, + "requestHeaders": [], + "validStatusCodeRanges": [ + "200" + ] + }, + "name": "HTTP Test", + "protocol": "Http", + "successThreshold": { + "checksFailedPercent": 5, + "roundTripTimeMs": 100 + }, + "testFrequencySec": 30 + } + ], + "testGroups": [ + { + "destinations": [ + "Office Portal" + ], + "disable": false, + "name": "TestHTTPBing", + "sources": [ + "<>-az-subnet-x-001(validation-rg)" + ], + "testConfigurations": [ + "HTTP Test" + ] + } + ], + "workspaceResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + ] + }, + "flowLogs": { + "value": [ + { + "enabled": false, + "storageId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "targetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001" + }, + { + "formatVersion": 1, + "name": "adp-<>-az-nsg-x-apgw-flowlog", + "retentionInDays": 8, + "storageId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "targetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-apgw", + "trafficAnalyticsInterval": 10, + "workspaceResourceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + ] + }, + "name": { + "value": "adp-<>-az-nw-x-001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/networkWatchers/version.json b/modules/Microsoft.Network/networkWatchers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/networkWatchers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..1a94a5c --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 privateDnsZone 'Microsoft.Network/privateDnsZones@2018-09-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateDnsZone.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateDnsZone +}] diff --git a/modules/Microsoft.Network/privateDnsZones/.test/min.parameters.json b/modules/Microsoft.Network/privateDnsZones/.test/min.parameters.json new file mode 100644 index 0000000..d33fbd0 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/.test/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": "<>-az-privdns-x-001.com" + } + } +} diff --git a/modules/Microsoft.Network/privateDnsZones/.test/parameters.json b/modules/Microsoft.Network/privateDnsZones/.test/parameters.json new file mode 100644 index 0000000..8b3662c --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/.test/parameters.json @@ -0,0 +1,198 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-privdns-x-002.com" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "AAAA": { + "value": [ + { + "name": "AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334", + "ttl": 3600, + "aaaaRecords": [ + { + "ipv6Address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + ] + } + ] + }, + "A": { + "value": [ + { + "name": "A_10.240.4.4", + "ttl": 3600, + "aRecords": [ + { + "ipv4Address": "10.240.4.4" + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "CNAME": { + "value": [ + { + "name": "CNAME_test", + "ttl": 3600, + "cnameRecord": { + "cname": "test" + }, + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "MX": { + "value": [ + { + "name": "MX_contoso", + "ttl": 3600, + "mxRecords": [ + { + "exchange": "contoso.com", + "preference": 100 + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "PTR": { + "value": [ + { + "name": "PTR_contoso", + "ttl": 3600, + "ptrRecords": [ + { + "ptrdname": "contoso.com" + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "SOA": { + "value": [ + { + "name": "@", + "ttl": 3600, + "soaRecord": { + "email": "azureprivatedns-host.microsoft.com", + "expireTime": 2419200, + "host": "azureprivatedns.net", + "minimumTtl": 10, + "refreshTime": 3600, + "retryTime": 300, + "serialNumber": "1" + }, + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "SRV": { + "value": [ + { + "name": "SRV_contoso", + "ttl": 3600, + "srvRecords": [ + { + "port": 9332, + "priority": 0, + "target": "test.contoso.com", + "weight": 0 + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "TXT": { + "value": [ + { + "name": "TXT_test", + "ttl": 3600, + "txtRecords": [ + { + "value": [ + "test" + ] + } + ], + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + ] + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001", + "registrationEnabled": true + } + ] + } + } +} diff --git a/modules/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..50f4d1b --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 A 'Microsoft.Network/privateDnsZones/A@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(A.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: A +}] diff --git a/modules/Microsoft.Network/privateDnsZones/A/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/A/deploy.bicep new file mode 100644 index 0000000..8d37144 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/A/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the A record.') +param name string + +@description('Optional. The list of A records in the record set.') +param aRecords array = [] + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource A 'Microsoft.Network/privateDnsZones/A@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + aRecords: aRecords + metadata: metadata + ttl: ttl + } +} + +module A_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: A.id + } +}] + +@description('The name of the deployed A record.') +output name string = A.name + +@description('The resource ID of the deployed A record.') +output resourceId string = A.id + +@description('The resource group of the deployed A record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/A/readme.md b/modules/Microsoft.Network/privateDnsZones/A/readme.md new file mode 100644 index 0000000..4883174 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/A/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone A record `[Microsoft.Network/privateDnsZones/A]` + +This module deploys a Private DNS Zone A record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/A` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the A record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `aRecords` | array | `[]` | The list of A records in the record set. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed A record. | +| `resourceGroupName` | string | The resource group of the deployed A record. | +| `resourceId` | string | The resource ID of the deployed A record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/A/version.json b/modules/Microsoft.Network/privateDnsZones/A/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/A/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..4ee1ab1 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 AAAA 'Microsoft.Network/privateDnsZones/AAAA@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(AAAA.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: AAAA +}] diff --git a/modules/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep new file mode 100644 index 0000000..41ee28a --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the AAAA record.') +param name string + +@description('Optional. The list of AAAA records in the record set.') +param aaaaRecords array = [] + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource AAAA 'Microsoft.Network/privateDnsZones/AAAA@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + aaaaRecords: aaaaRecords + metadata: metadata + ttl: ttl + } +} + +module AAAA_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSAAAA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: AAAA.id + } +}] + +@description('The name of the deployed AAAA record.') +output name string = AAAA.name + +@description('The resource ID of the deployed AAAA record.') +output resourceId string = AAAA.id + +@description('The resource group of the deployed AAAA record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/AAAA/readme.md b/modules/Microsoft.Network/privateDnsZones/AAAA/readme.md new file mode 100644 index 0000000..08e641d --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/AAAA/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone AAAA record `[Microsoft.Network/privateDnsZones/AAAA]` + +This module deploys a Private DNS Zone AAAA record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/AAAA` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the AAAA record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `aaaaRecords` | array | `[]` | The list of AAAA records in the record set. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed AAAA record. | +| `resourceGroupName` | string | The resource group of the deployed AAAA record. | +| `resourceId` | string | The resource ID of the deployed AAAA record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/AAAA/version.json b/modules/Microsoft.Network/privateDnsZones/AAAA/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/AAAA/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a06a5c2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,73 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 CNAME 'Microsoft.Network/privateDnsZones/CNAME@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(CNAME.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: CNAME +}] + +output id string = roleAssignment[0].name diff --git a/modules/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep new file mode 100644 index 0000000..ccf10de --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the CNAME record.') +param name string + +@description('Optional. A CNAME record.') +param cnameRecord object = {} + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource CNAME 'Microsoft.Network/privateDnsZones/CNAME@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + cnameRecord: cnameRecord + metadata: metadata + ttl: ttl + } +} + +module CNAME_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSCNAME-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: CNAME.id + } +}] + +@description('The name of the deployed CNAME record.') +output name string = CNAME.name + +@description('The resource ID of the deployed CNAME record.') +output resourceId string = CNAME.id + +@description('The resource group of the deployed CNAME record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/CNAME/readme.md b/modules/Microsoft.Network/privateDnsZones/CNAME/readme.md new file mode 100644 index 0000000..a7c6c05 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/CNAME/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone CNAME record `[Microsoft.Network/privateDnsZones/CNAME]` + +This module deploys a Private DNS Zone CNAME record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/CNAME` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the CNAME record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cnameRecord` | object | `{object}` | A CNAME record. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed CNAME record. | +| `resourceGroupName` | string | The resource group of the deployed CNAME record. | +| `resourceId` | string | The resource ID of the deployed CNAME record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/CNAME/version.json b/modules/Microsoft.Network/privateDnsZones/CNAME/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/CNAME/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..38aee42 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 MX 'Microsoft.Network/privateDnsZones/MX@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(MX.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: MX +}] diff --git a/modules/Microsoft.Network/privateDnsZones/MX/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/MX/deploy.bicep new file mode 100644 index 0000000..ccfaa8a --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/MX/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the MX record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of MX records in the record set.') +param mxRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource MX 'Microsoft.Network/privateDnsZones/MX@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + mxRecords: mxRecords + ttl: ttl + } +} + +module MX_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSMX-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: MX.id + } +}] + +@description('The name of the deployed MX record.') +output name string = MX.name + +@description('The resource ID of the deployed MX record.') +output resourceId string = MX.id + +@description('The resource group of the deployed MX record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/MX/readme.md b/modules/Microsoft.Network/privateDnsZones/MX/readme.md new file mode 100644 index 0000000..4550225 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/MX/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone MX record `[Microsoft.Network/privateDnsZones/MX]` + +This module deploys a Private DNS Zone MX record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/MX` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the MX record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `mxRecords` | array | `[]` | The list of MX records in the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed MX record. | +| `resourceGroupName` | string | The resource group of the deployed MX record. | +| `resourceId` | string | The resource ID of the deployed MX record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/MX/version.json b/modules/Microsoft.Network/privateDnsZones/MX/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/MX/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..32c5b4b --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 PTR 'Microsoft.Network/privateDnsZones/PTR@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(PTR.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: PTR +}] diff --git a/modules/Microsoft.Network/privateDnsZones/PTR/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/PTR/deploy.bicep new file mode 100644 index 0000000..0cffc9b --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/PTR/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the PTR record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of PTR records in the record set.') +param ptrRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module PTR_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSPTR-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: PTR.id + } +}] + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource PTR 'Microsoft.Network/privateDnsZones/PTR@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + ptrRecords: ptrRecords + ttl: ttl + } +} + +@description('The name of the deployed PTR record.') +output name string = PTR.name + +@description('The resource ID of the deployed PTR record.') +output resourceId string = PTR.id + +@description('The resource group of the deployed PTR record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/PTR/readme.md b/modules/Microsoft.Network/privateDnsZones/PTR/readme.md new file mode 100644 index 0000000..efbef03 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/PTR/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone PTR record `[Microsoft.Network/privateDnsZones/PTR]` + +This module deploys a Private DNS Zone PTR record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/PTR` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the PTR record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `ptrRecords` | array | `[]` | The list of PTR records in the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed PTR record. | +| `resourceGroupName` | string | The resource group of the deployed PTR record. | +| `resourceId` | string | The resource ID of the deployed PTR record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/PTR/version.json b/modules/Microsoft.Network/privateDnsZones/PTR/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/PTR/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..1152c56 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 SOA 'Microsoft.Network/privateDnsZones/SOA@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(SOA.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: SOA +}] diff --git a/modules/Microsoft.Network/privateDnsZones/SOA/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/SOA/deploy.bicep new file mode 100644 index 0000000..23a76cb --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SOA/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the SOA record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. A SOA record.') +param soaRecord object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource SOA 'Microsoft.Network/privateDnsZones/SOA@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + soaRecord: soaRecord + ttl: ttl + } +} + +module SOA_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSSOA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: SOA.id + } +}] + +@description('The name of the deployed SOA record.') +output name string = SOA.name + +@description('The resource ID of the deployed SOA record.') +output resourceId string = SOA.id + +@description('The resource group of the deployed SOA record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/SOA/readme.md b/modules/Microsoft.Network/privateDnsZones/SOA/readme.md new file mode 100644 index 0000000..550afa2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SOA/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone SOA record `[Microsoft.Network/privateDnsZones/SOA]` + +This module deploys a Private DNS Zone SOA record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/SOA` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SOA record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `soaRecord` | object | `{object}` | A SOA record. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed SOA record. | +| `resourceGroupName` | string | The resource group of the deployed SOA record. | +| `resourceId` | string | The resource ID of the deployed SOA record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/SOA/version.json b/modules/Microsoft.Network/privateDnsZones/SOA/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SOA/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..e3e496c --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 SRV 'Microsoft.Network/privateDnsZones/SRV@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(SRV.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: SRV +}] diff --git a/modules/Microsoft.Network/privateDnsZones/SRV/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/SRV/deploy.bicep new file mode 100644 index 0000000..a037a1d --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SRV/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the SRV record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of SRV records in the record set.') +param srvRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource SRV 'Microsoft.Network/privateDnsZones/SRV@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + srvRecords: srvRecords + ttl: ttl + } +} + +module SRV_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSSRV-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: SRV.id + } +}] + +@description('The name of the deployed SRV record.') +output name string = SRV.name + +@description('The resource ID of the deployed SRV record.') +output resourceId string = SRV.id + +@description('The resource group of the deployed SRV record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/SRV/readme.md b/modules/Microsoft.Network/privateDnsZones/SRV/readme.md new file mode 100644 index 0000000..26b9270 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SRV/readme.md @@ -0,0 +1,110 @@ +# Private DNS Zone SRV record `[Microsoft.Network/privateDnsZones/SRV]` + +This module deploys a Private DNS Zone TXT record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/SRV` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SRV record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `srvRecords` | array | `[]` | The list of SRV records in the record set. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed SRV record. | +| `resourceGroupName` | string | The resource group of the deployed SRV record. | +| `resourceId` | string | The resource ID of the deployed SRV record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/SRV/version.json b/modules/Microsoft.Network/privateDnsZones/SRV/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/SRV/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..6f823b4 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + '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 TXT 'Microsoft.Network/privateDnsZones/TXT@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(TXT.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: TXT +}] diff --git a/modules/Microsoft.Network/privateDnsZones/TXT/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/TXT/deploy.bicep new file mode 100644 index 0000000..2e12ffc --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/TXT/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the TXT record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. The list of TXT records in the record set.') +param txtRecords array = [] + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource TXT 'Microsoft.Network/privateDnsZones/TXT@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + ttl: ttl + txtRecords: txtRecords + } +} + +module TXT_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSTXT-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: TXT.id + } +}] + +@description('The name of the deployed TXT record.') +output name string = TXT.name + +@description('The resource ID of the deployed TXT record.') +output resourceId string = TXT.id + +@description('The resource group of the deployed TXT record.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateDnsZones/TXT/readme.md b/modules/Microsoft.Network/privateDnsZones/TXT/readme.md new file mode 100644 index 0000000..903615d --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/TXT/readme.md @@ -0,0 +1,143 @@ +# Private DNS Zone TXT record `[Microsoft.Network/privateDnsZones/TXT]` + +This module deploys a Private DNS Zone TXT record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones/TXT` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the TXT record. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | 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'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | +| `txtRecords` | array | `[]` | The list of TXT records in the record set. | + + +### Parameter Usage: `txtRecords` + +

+ +Parameter JSON format + +```json +"txtRecords": { + "value": [ + { + "value": [ "string" ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +txtRecords: [ + { + value: [ 'string' ] + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed TXT record. | +| `resourceGroupName` | string | The resource group of the deployed TXT record. | +| `resourceId` | string | The resource ID of the deployed TXT record. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/TXT/version.json b/modules/Microsoft.Network/privateDnsZones/TXT/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/TXT/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/deploy.bicep new file mode 100644 index 0000000..9c8616e --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/deploy.bicep @@ -0,0 +1,220 @@ +@description('Required. Private DNS zone name.') +param name string + +@description('Optional. Array of A records.') +param a array = [] + +@description('Optional. Array of AAAA records.') +param aaaa array = [] + +@description('Optional. Array of CNAME records.') +param cname array = [] + +@description('Optional. Array of MX records.') +param mx array = [] + +@description('Optional. Array of PTR records.') +param ptr array = [] + +@description('Optional. Array of SOA records.') +param soa array = [] + +@description('Optional. Array of SRV records.') +param srv array = [] + +@description('Optional. Array of TXT records.') +param txt array = [] + +@description('Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties \'vnetResourceId\' and \'registrationEnabled\'. The \'vnetResourceId\' is a resource ID of a vNet to link, \'registrationEnabled\' (bool) enables automatic DNS registration in the zone for the linked vNet.') +param virtualNetworkLinks array = [] + +@description('Optional. The location of the PrivateDNSZone. Should be global.') +param location string = 'global' + +@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. Tags of the resource.') +param tags object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: name + location: location + tags: tags +} + +module privateDnsZone_A 'A/deploy.bicep' = [for (aRecord, index) in a: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-ARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: aRecord.name + aRecords: contains(aRecord, 'aRecords') ? aRecord.aRecords : [] + metadata: contains(aRecord, 'metadata') ? aRecord.metadata : {} + ttl: contains(aRecord, 'ttl') ? aRecord.ttl : 3600 + roleAssignments: contains(aRecord, 'roleAssignments') ? aRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_AAAA 'AAAA/deploy.bicep' = [for (aaaaRecord, index) in aaaa: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-AAAARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: aaaaRecord.name + aaaaRecords: contains(aaaaRecord, 'aaaaRecords') ? aaaaRecord.aaaaRecords : [] + metadata: contains(aaaaRecord, 'metadata') ? aaaaRecord.metadata : {} + ttl: contains(aaaaRecord, 'ttl') ? aaaaRecord.ttl : 3600 + roleAssignments: contains(aaaaRecord, 'roleAssignments') ? aaaaRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_CNAME 'CNAME/deploy.bicep' = [for (cnameRecord, index) in cname: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-CNAMERecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: cnameRecord.name + cnameRecord: contains(cnameRecord, 'cnameRecord') ? cnameRecord.cnameRecord : {} + metadata: contains(cnameRecord, 'metadata') ? cnameRecord.metadata : {} + ttl: contains(cnameRecord, 'ttl') ? cnameRecord.ttl : 3600 + roleAssignments: contains(cnameRecord, 'roleAssignments') ? cnameRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_MX 'MX/deploy.bicep' = [for (mxRecord, index) in mx: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-MXRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: mxRecord.name + metadata: contains(mxRecord, 'metadata') ? mxRecord.metadata : {} + mxRecords: contains(mxRecord, 'mxRecords') ? mxRecord.mxRecords : [] + ttl: contains(mxRecord, 'ttl') ? mxRecord.ttl : 3600 + roleAssignments: contains(mxRecord, 'roleAssignments') ? mxRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_PTR 'PTR/deploy.bicep' = [for (ptrRecord, index) in ptr: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-PTRRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: ptrRecord.name + metadata: contains(ptrRecord, 'metadata') ? ptrRecord.metadata : {} + ptrRecords: contains(ptrRecord, 'ptrRecords') ? ptrRecord.ptrRecords : [] + ttl: contains(ptrRecord, 'ttl') ? ptrRecord.ttl : 3600 + roleAssignments: contains(ptrRecord, 'roleAssignments') ? ptrRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_SOA 'SOA/deploy.bicep' = [for (soaRecord, index) in soa: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-SOARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: soaRecord.name + metadata: contains(soaRecord, 'metadata') ? soaRecord.metadata : {} + soaRecord: contains(soaRecord, 'soaRecord') ? soaRecord.soaRecord : {} + ttl: contains(soaRecord, 'ttl') ? soaRecord.ttl : 3600 + roleAssignments: contains(soaRecord, 'roleAssignments') ? soaRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_SRV 'SRV/deploy.bicep' = [for (srvRecord, index) in srv: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-SRVRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: srvRecord.name + metadata: contains(srvRecord, 'metadata') ? srvRecord.metadata : {} + srvRecords: contains(srvRecord, 'srvRecords') ? srvRecord.srvRecords : [] + ttl: contains(srvRecord, 'ttl') ? srvRecord.ttl : 3600 + roleAssignments: contains(srvRecord, 'roleAssignments') ? srvRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_TXT 'TXT/deploy.bicep' = [for (txtRecord, index) in txt: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-TXTRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: txtRecord.name + metadata: contains(txtRecord, 'metadata') ? txtRecord.metadata : {} + txtRecords: contains(txtRecord, 'txtRecords') ? txtRecord.txtRecords : [] + ttl: contains(txtRecord, 'ttl') ? txtRecord.ttl : 3600 + roleAssignments: contains(txtRecord, 'roleAssignments') ? txtRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_virtualNetworkLinks 'virtualNetworkLinks/deploy.bicep' = [for (virtualNetworkLink, index) in virtualNetworkLinks: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-VirtualNetworkLink-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: contains(virtualNetworkLink, 'name') ? virtualNetworkLink.name : '${last(split(virtualNetworkLink.virtualNetworkResourceId, '/'))}-vnetlink' + virtualNetworkResourceId: virtualNetworkLink.virtualNetworkResourceId + location: contains(virtualNetworkLink, 'location') ? virtualNetworkLink.location : 'global' + registrationEnabled: contains(virtualNetworkLink, 'registrationEnabled') ? virtualNetworkLink.registrationEnabled : false + tags: contains(virtualNetworkLink, 'tags') ? virtualNetworkLink.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource privateDnsZone_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${privateDnsZone.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateDnsZone +} + +module privateDnsZone_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateDnsZone.id + } +}] + +@description('The resource group the private DNS zone was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the private DNS zone.') +output name string = privateDnsZone.name + +@description('The resource ID of the private DNS zone.') +output resourceId string = privateDnsZone.id + +@description('The location the resource was deployed into.') +output location string = privateDnsZone.location diff --git a/modules/Microsoft.Network/privateDnsZones/readme.md b/modules/Microsoft.Network/privateDnsZones/readme.md new file mode 100644 index 0000000..7c90f9d --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/readme.md @@ -0,0 +1,608 @@ +# Private DNS Zones `[Microsoft.Network/privateDnsZones]` + +This template deploys a private DNS zone. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateDnsZones` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones) | +| `Microsoft.Network/privateDnsZones/A` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | +| `Microsoft.Network/privateDnsZones/AAAA` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | +| `Microsoft.Network/privateDnsZones/CNAME` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | +| `Microsoft.Network/privateDnsZones/MX` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | +| `Microsoft.Network/privateDnsZones/PTR` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | +| `Microsoft.Network/privateDnsZones/SOA` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | +| `Microsoft.Network/privateDnsZones/SRV` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | +| `Microsoft.Network/privateDnsZones/TXT` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Private DNS zone name. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `a` | _[a](a/readme.md)_ array | `[]` | | Array of A records. | +| `aaaa` | _[aaaa](aaaa/readme.md)_ array | `[]` | | Array of AAAA records. | +| `cname` | _[cname](cname/readme.md)_ array | `[]` | | Array of CNAME records. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `'global'` | | The location of the PrivateDNSZone. Should be global. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `mx` | _[mx](mx/readme.md)_ array | `[]` | | Array of MX records. | +| `ptr` | _[ptr](ptr/readme.md)_ array | `[]` | | Array of PTR records. | +| `roleAssignments` | array | `[]` | | 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'. | +| `soa` | _[soa](soa/readme.md)_ array | `[]` | | Array of SOA records. | +| `srv` | _[srv](srv/readme.md)_ array | `[]` | | Array of SRV records. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `txt` | _[txt](txt/readme.md)_ array | `[]` | | Array of TXT records. | +| `virtualNetworkLinks` | _[virtualNetworkLinks](virtualNetworkLinks/readme.md)_ array | `[]` | | Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'vnetResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private DNS zone. | +| `resourceGroupName` | string | The resource group the private DNS zone was deployed into. | +| `resourceId` | string | The resource ID of the private DNS zone. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module privateDnsZones './Microsoft.Network/privateDnsZones/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateDnsZones' + params: { + name: '<>-az-privdns-x-001.com' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-privdns-x-001.com" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module privateDnsZones './Microsoft.Network/privateDnsZones/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateDnsZones' + params: { + // Required parameters + name: '<>-az-privdns-x-002.com' + // Non-required parameters + A: [ + { + aRecords: [ + { + ipv4Address: '10.240.4.4' + } + ] + name: 'A_10.240.4.4' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + AAAA: [ + { + aaaaRecords: [ + { + ipv6Address: '2001:0db8:85a3:0000:0000:8a2e:0370:7334' + } + ] + name: 'AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334' + ttl: 3600 + } + ] + CNAME: [ + { + cnameRecord: { + cname: 'test' + } + name: 'CNAME_test' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + lock: 'CanNotDelete' + MX: [ + { + mxRecords: [ + { + exchange: 'contoso.com' + preference: 100 + } + ] + name: 'MX_contoso' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + PTR: [ + { + name: 'PTR_contoso' + ptrRecords: [ + { + ptrdname: 'contoso.com' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + SOA: [ + { + name: '@' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + soaRecord: { + email: 'azureprivatedns-host.microsoft.com' + expireTime: 2419200 + host: 'azureprivatedns.net' + minimumTtl: 10 + refreshTime: 3600 + retryTime: 300 + serialNumber: '1' + } + ttl: 3600 + } + ] + SRV: [ + { + name: 'SRV_contoso' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + srvRecords: [ + { + port: 9332 + priority: 0 + target: 'test.contoso.com' + weight: 0 + } + ] + ttl: 3600 + } + ] + TXT: [ + { + name: 'TXT_test' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + txtRecords: [ + { + value: [ + 'test' + ] + } + ] + } + ] + virtualNetworkLinks: [ + { + registrationEnabled: true + virtualNetworkResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-privdns-x-002.com" + }, + // Non-required parameters + "A": { + "value": [ + { + "aRecords": [ + { + "ipv4Address": "10.240.4.4" + } + ], + "name": "A_10.240.4.4", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "AAAA": { + "value": [ + { + "aaaaRecords": [ + { + "ipv6Address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + ], + "name": "AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334", + "ttl": 3600 + } + ] + }, + "CNAME": { + "value": [ + { + "cnameRecord": { + "cname": "test" + }, + "name": "CNAME_test", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "MX": { + "value": [ + { + "mxRecords": [ + { + "exchange": "contoso.com", + "preference": 100 + } + ], + "name": "MX_contoso", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "PTR": { + "value": [ + { + "name": "PTR_contoso", + "ptrRecords": [ + { + "ptrdname": "contoso.com" + } + ], + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "SOA": { + "value": [ + { + "name": "@", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "soaRecord": { + "email": "azureprivatedns-host.microsoft.com", + "expireTime": 2419200, + "host": "azureprivatedns.net", + "minimumTtl": 10, + "refreshTime": 3600, + "retryTime": 300, + "serialNumber": "1" + }, + "ttl": 3600 + } + ] + }, + "SRV": { + "value": [ + { + "name": "SRV_contoso", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "srvRecords": [ + { + "port": 9332, + "priority": 0, + "target": "test.contoso.com", + "weight": 0 + } + ], + "ttl": 3600 + } + ] + }, + "TXT": { + "value": [ + { + "name": "TXT_test", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600, + "txtRecords": [ + { + "value": [ + "test" + ] + } + ] + } + ] + }, + "virtualNetworkLinks": { + "value": [ + { + "registrationEnabled": true, + "virtualNetworkResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/privateDnsZones/version.json b/modules/Microsoft.Network/privateDnsZones/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep new file mode 100644 index 0000000..1596252 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep @@ -0,0 +1,61 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Optional. The name of the virtual network link.') +param name string = '${last(split(virtualNetworkResourceId, '/'))}-vnetlink' + +@description('Optional. The location of the PrivateDNSZone. Should be global.') +param location string = 'global' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?.') +param registrationEnabled bool = false + +@description('Required. Link to another virtual network resource ID.') +param virtualNetworkResourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource virtualNetworkLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: name + parent: privateDnsZone + location: location + tags: tags + properties: { + registrationEnabled: registrationEnabled + virtualNetwork: { + id: virtualNetworkResourceId + } + } +} + +@description('The name of the deployed virtual network link.') +output name string = virtualNetworkLink.name + +@description('The resource ID of the deployed virtual network link.') +output resourceId string = virtualNetworkLink.id + +@description('The resource group of the deployed virtual network link.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = virtualNetworkLink.location diff --git a/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md new file mode 100644 index 0000000..db28d96 --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md @@ -0,0 +1,92 @@ +# Private DNS Zone Virtual Network Link `[Microsoft.Network/privateDnsZones/virtualNetworkLinks]` + +This module deploys private dns zone virtual network links. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualNetworkResourceId` | string | Link to another virtual network resource ID. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `'global'` | The location of the PrivateDNSZone. Should be global. | +| `name` | string | `[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]` | The name of the virtual network link. | +| `registrationEnabled` | bool | `False` | Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?. | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed virtual network link. | +| `resourceGroupName` | string | The resource group of the deployed virtual network link. | +| `resourceId` | string | The resource ID of the deployed virtual network link. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..0c71002 --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 privateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateEndpoint.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateEndpoint +}] diff --git a/modules/Microsoft.Network/privateEndpoints/.test/min.parameters.json b/modules/Microsoft.Network/privateEndpoints/.test/min.parameters.json new file mode 100644 index 0000000..aa3ea8e --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/.test/min.parameters.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pe-kvlt-min-001" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + "serviceResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe" + }, + "groupIds": { + "value": [ + "vault" + ] + } + } +} diff --git a/modules/Microsoft.Network/privateEndpoints/.test/parameters.json b/modules/Microsoft.Network/privateEndpoints/.test/parameters.json new file mode 100644 index 0000000..b6cadbe --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/.test/parameters.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pe-kvlt-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + "serviceResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe" + }, + "groupIds": { + "value": [ + "vault" + ] + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net" + ] + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/privateEndpoints/deploy.bicep b/modules/Microsoft.Network/privateEndpoints/deploy.bicep new file mode 100644 index 0000000..d500094 --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/deploy.bicep @@ -0,0 +1,119 @@ +@description('Required. Name of the private endpoint resource to create.') +param name string + +@description('Required. Resource ID of the subnet where the endpoint needs to be created.') +param subnetResourceId string + +@description('Required. Resource ID of the resource that needs to be connected to the network.') +param serviceResourceId string + +@description('Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to.') +param groupIds array + +@description('Optional. The private DNS zone group configuration used to associate the private endpoint with one or multiple private DNS zones. A DNS zone group can support up to 5 DNS zones.') +param privateDnsZoneGroup object = {} + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags to be applied on all resources/resource groups in this deployment.') +param tags object = {} + +@description('Optional. Custom DNS configurations.') +param customDnsConfigs array = [] + +@description('Optional. Manual PrivateLink Service Connections.') +param manualPrivateLinkServiceConnections array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: serviceResourceId + groupIds: groupIds + } + } + ] + manualPrivateLinkServiceConnections: manualPrivateLinkServiceConnections + subnet: { + id: subnetResourceId + } + customDnsConfigs: customDnsConfigs + } +} + +module privateEndpoint_privateDnsZoneGroup 'privateDnsZoneGroups/deploy.bicep' = if (!empty(privateDnsZoneGroup)) { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-PrivateDnsZoneGroup' + params: { + privateDNSResourceIds: privateDnsZoneGroup.privateDNSResourceIds + privateEndpointName: privateEndpoint.name + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource privateEndpoint_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${privateEndpoint.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateEndpoint +} + +module privateEndpoint_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateEndpoint.id + } +}] + +@description('The resource group the private endpoint was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the private endpoint.') +output resourceId string = privateEndpoint.id + +@description('The name of the private endpoint.') +output name string = privateEndpoint.name + +@description('The location the resource was deployed into.') +output location string = privateEndpoint.location diff --git a/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep new file mode 100644 index 0000000..5ceb1ce --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep @@ -0,0 +1,53 @@ +@description('Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment.') +param privateEndpointName string + +@description('Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones.') +@minLength(1) +@maxLength(5) +param privateDNSResourceIds array + +@description('Optional. The name of the private DNS zone group.') +param name string = 'default' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var privateDnsZoneConfigs = [for privateDNSResourceId in privateDNSResourceIds: { + name: last(split(privateDNSResourceId, '/')) + properties: { + privateDnsZoneId: privateDNSResourceId + } +}] + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' existing = { + name: privateEndpointName +} + +resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = { + name: name + parent: privateEndpoint + properties: { + privateDnsZoneConfigs: privateDnsZoneConfigs + } +} + +@description('The name of the private endpoint DNS zone group.') +output name string = privateDnsZoneGroup.name + +@description('The resource ID of the private endpoint DNS zone group.') +output resourceId string = privateDnsZoneGroup.id + +@description('The resource group the private endpoint DNS zone group was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md new file mode 100644 index 0000000..1cecdaa --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md @@ -0,0 +1,47 @@ +# Network Private Endpoint Private DNS Zone Group `[Microsoft.Network/privateEndpoints/privateDnsZoneGroups]` + +This module deploys a private endpoint private DNS zone group + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDNSResourceIds` | array | Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateEndpointName` | string | The name of the parent private endpoint. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | The name of the private DNS zone group. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the private endpoint DNS zone group. | +| `resourceGroupName` | string | The resource group the private endpoint DNS zone group was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint DNS zone group. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateEndpoints/readme.md b/modules/Microsoft.Network/privateEndpoints/readme.md new file mode 100644 index 0000000..16798e0 --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/readme.md @@ -0,0 +1,319 @@ +# Private Endpoints `[Microsoft.Network/privateEndpoints]` + +This template deploys a private endpoint for a generic service. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | + +### Resource dependency + +The following resources are required to be able to deploy this resource: + +- `PrivateDNSZone` +- `VirtualNetwork/subnet` +- The service that needs to be connected through private endpoint + +**Important**: 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). + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `groupIds` | array | Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to. | +| `name` | string | Name of the private endpoint resource to create. | +| `serviceResourceId` | string | Resource ID of the resource that needs to be connected to the network. | +| `subnetResourceId` | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `customDnsConfigs` | array | `[]` | | Custom DNS configurations. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `manualPrivateLinkServiceConnections` | array | `[]` | | Manual PrivateLink Service Connections. | +| `privateDnsZoneGroup` | object | `{object}` | | The private DNS zone group configuration used to associate the private endpoint with one or multiple private DNS zones. A DNS zone group can support up to 5 DNS zones. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags to be applied on all resources/resource groups in this deployment. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private endpoint. | +| `resourceGroupName` | string | The resource group the private endpoint was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module privateEndpoints './Microsoft.Network/privateEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateEndpoints' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: '<>-az-pe-kvlt-min-001' + serviceResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } +} +``` + +
+

+ +

+ +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 + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "<>-az-pe-kvlt-min-001" + }, + "serviceResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module privateEndpoints './Microsoft.Network/privateEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateEndpoints' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: '<>-az-pe-kvlt-001' + serviceResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + // Non-required parameters + lock: 'CanNotDelete' + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net' + ] + } + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "<>-az-pe-kvlt-001" + }, + "serviceResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-pe" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net" + ] + } + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/privateEndpoints/version.json b/modules/Microsoft.Network/privateEndpoints/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Network/privateEndpoints/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..894f8e2 --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 privateLinkService 'Microsoft.Network/privateLinkServices@2022-01-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateLinkService.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateLinkService +}] diff --git a/modules/Microsoft.Network/privateLinkServices/.test/min.parameters.json b/modules/Microsoft.Network/privateLinkServices/.test/min.parameters.json new file mode 100644 index 0000000..95a5a5c --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/.test/min.parameters.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pls-min-001" + }, + "ipConfigurations": { + "value": [ + { + "name": "minpls01", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls-min" + } + ] + } + } +} diff --git a/modules/Microsoft.Network/privateLinkServices/.test/parameters.json b/modules/Microsoft.Network/privateLinkServices/.test/parameters.json new file mode 100644 index 0000000..8390d3e --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/.test/parameters.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pls-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "ipConfigurations": { + "value": [ + { + "name": "pls01", + "properties": { + "primary": true, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls" + } + ] + }, + "autoApproval": { + "value": { + "subscriptions": [ + "*" + ] + } + }, + "visibility": { + "value": { + "subscriptions": [ + "<>" + ] + } + }, + "enableProxyProtocol": { + "value": true + }, + "fqdns": { + "value": [ + "<>.plsfqdn01.azure.privatelinkservice", + "<>.plsfqdn02.azure.privatelinkserivce" + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/privateLinkServices/deploy.bicep b/modules/Microsoft.Network/privateLinkServices/deploy.bicep new file mode 100644 index 0000000..5746d4d --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/deploy.bicep @@ -0,0 +1,104 @@ +@description('Required. Name of the private link service to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags to be applied on all resources/resource groups in this deployment.') +param tags object = {} + +@description('Optional. The extended location of the load balancer.') +param extendedLocation object = {} + +@description('Optional. The auto-approval list of the private link service.') +param autoApproval object = {} + +@description('Optional. Whether the private link service is enabled for proxy protocol or not.') +param enableProxyProtocol bool = false + +@description('Optional. The list of Fqdn.') +param fqdns array = [] + +@description('Optional. An array of private link service IP configurations.') +param ipConfigurations array = [] + +@description('Optional. An array of references to the load balancer IP configurations.') +param loadBalancerFrontendIpConfigurations array = [] + +@description('Optional. The visibility list of the private link service.') +param visibility object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkService 'Microsoft.Network/privateLinkServices@2022-01-01' = { + name: name + location: location + tags: tags + extendedLocation: !empty(extendedLocation) ? extendedLocation : null + properties: { + autoApproval: autoApproval + enableProxyProtocol: enableProxyProtocol + fqdns: fqdns + ipConfigurations: ipConfigurations + loadBalancerFrontendIpConfigurations: loadBalancerFrontendIpConfigurations + visibility: visibility + } +} + +resource privateLinkService_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${privateLinkService.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkService +} + +module privateLinkService_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateLinkService-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateLinkService.id + } +}] + +@description('The resource group the private link service was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the private link service.') +output resourceId string = privateLinkService.id + +@description('The name of the private link service.') +output name string = privateLinkService.name + +@description('The location the resource was deployed into.') +output location string = privateLinkService.location diff --git a/modules/Microsoft.Network/privateLinkServices/readme.md b/modules/Microsoft.Network/privateLinkServices/readme.md new file mode 100644 index 0000000..81136df --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/readme.md @@ -0,0 +1,646 @@ +# Network PrivateLinkServices `[Microsoft.Network/privateLinkServices]` + +This module deploys Network Private Link Services. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateLinkServices` | [2022-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-01-01/privateLinkServices) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the private link service to create. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `autoApproval` | object | `{object}` | | The auto-approval list of the private link service. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableProxyProtocol` | bool | `False` | | Whether the private link service is enabled for proxy protocol or not. | +| `extendedLocation` | object | `{object}` | | The extended location of the load balancer. | +| `fqdns` | array | `[]` | | The list of Fqdn. | +| `ipConfigurations` | array | `[]` | | An array of private link service IP configurations. | +| `loadBalancerFrontendIpConfigurations` | array | `[]` | | An array of references to the load balancer IP configurations. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags to be applied on all resources/resource groups in this deployment. | +| `visibility` | object | `{object}` | | The visibility list of the private link service. | + + +### Parameter Usage: `ipConfigurations` + +This property refers to the NAT (Network Address Translation) IP configuration for the Private Link service. The NAT IP can be chosen from any subnet in a service provider's virtual network. Private Link service performs destination side NAT-ing on the Private Link traffic. This ensures that there is no IP conflict between source (consumer side) and destination (service provider) address space. On the destination side (service provider side), the NAT IP address will show up as Source IP for all packets received by your service and destination IP for all packets sent by your service. + +

+ +Parameter JSON format + +```json +"ipConfigurations": { + "value": [ + // Example showing only mandatory fields + { + "name": "minpls01", // Name of the IP configuration + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + }, + // Example showing commonly used fields + { + "name": "pls01", // Name of the IP configuration + "properties": { + "primary": false, // Whether the ip configuration is primary or not + "privateIPAddressVersion": "IPv4", // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + "privateIPAllocationMethod": "Static", // The private IP address allocation method + "privateIPAddress": "10.0.1.10", // If "privateIPAllocationMethod" is set to "Static" then this needs to be supplied + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +ipConfigurations: [ + // Example showing only mandatory fields + { + name: 'minpls01' // Name of the IP configuration + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } + // Example showing commonly used fields + { + name: 'pls01' // Name of the IP configuration + properties: { + primary: false // Whether the ip configuration is primary or not + privateIPAddressVersion: 'IPv4' // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + privateIPAllocationMethod: 'Static' // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + privateIPAddress: '10.0.1.10' // If "privateIPAllocationMethod" is set to "Static" then this needs to be supplied + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } +] +``` + +
+

+ +### Parameter Usage: `loadBalancerFrontendIpConfigurations` + +Private Link service is tied to the frontend IP address of a Standard Load Balancer. All traffic destined for the service will reach the frontend of the SLB. You can configure SLB rules to direct this traffic to appropriate backend pools where your applications are running. Load balancer frontend IP configurations are different than NAT IP configurations. + +

+ +Parameter JSON format + +```json +"loadBalancerFrontendIpConfigurations": { + "value": [ + // Example showing reference to the font end IP configuration of the load balancer + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/frontendIPConfigurations/privateIPConfig1" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +loadBalancerFrontendIpConfigurations: [ + // Example showing reference to the font end IP configuration of the load balancer + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/frontendIPConfigurations/privateIPConfig1' + } +] +``` + +
+

+ +### Parameter Usage: `extendedLocation` + +This is the Edge Zone ID of the Edge Zone corresponding to the region in which the resource is deployed. More information is available here: [Azure Edge Zone ID](https://docs.microsoft.com/en-us/azure/public-multi-access-edge-compute-mec/key-concepts#azure-edge-zone-id). + +

+ +Parameter JSON format + +```json +"extendedLocation": { + // Example showing usage of the extendedLocation param + "value": { + "name": "attatlanta1", // Edge Zone ID for the parent East US 2 region is "attatlanta1" + "type": "EdgeZone" // Fixed value + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extendedLocation: { + // Example showing usage of the extendedLocation param + name: 'attdallas1' // Edge Zone ID for the parent South Central US region is "attdallas1". + type: 'EdgeZone' // Fixed value +} +``` + +
+

+ +### Parameter Usage: `autoApproval` + +Auto-approval controls the automated access to the Private Link service. The subscriptions specified in the auto-approval list are approved automatically when a connection is requested from private endpoints in those subscriptions. + +

+ +Parameter JSON format + +```json +// Example to auto-approve for all the subscriptions present under the "visibility" param +"autoApproval": { + "value": [ + "*" + ] +} + +// Example to auto-approve a specific set of subscriptions. This should always be a subset of the subscriptions provided under the "visibility" param +"autoApproval": { + "value": [ + "12345678-1234-1234-1234-123456781234", // Subscription 1 + "87654321-1234-1234-1234-123456781234" // Subscription 2 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +// Example to auto-approve for all the subscriptions present under the "visibility" param +autoApproval: [ + "*" +] + +// Example to auto-approve a specific set of subscriptions. This should always be a subset of the subscriptions provided under "visibility" +autoApproval: [ + '12345678-1234-1234-1234-123456781234' // Subscription 1 + '87654321-1234-1234-1234-123456781234' // Subscription 2 +] +``` + +
+

+ +### Parameter Usage: `visibility` + +Visibility is the property that controls the exposure settings for your Private Link service. Service providers can choose to limit the exposure to their service to subscriptions with Azure role-based access control (Azure RBAC) permissions, a restricted set of subscriptions, or all Azure subscriptions. + +

+ +Parameter JSON format + +```json +"visibility": { + "value" + // Example showing usage of visibility param + "subscriptions": [ + "12345678-1234-1234-1234-123456781234", // Subscription 1 + "87654321-1234-1234-1234-123456781234", // Subscription 2 + "12341234-1234-1234-1234-123456781234" // Subscription 3 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +visibility: { + subscriptions: [ + '12345678-1234-1234-1234-123456781234' // Subscription 1 + '87654321-1234-1234-1234-123456781234' // Subscription 2 + '12341234-1234-1234-1234-123456781234' // Subscription 3 + ] +} +``` + +
+

+ +### Parameter Usage: `enableProxyProtocol` + +This property lets the service provider use tcp proxy v2 to retrieve connection information about the service consumer. Service Provider is responsible for setting up receiver configs to be able to parse the proxy protocol v2 header. + +### Parameter Usage: `fqdns` + +This property lets you set the fqdn(s) to access the Private Link service. +

+ +Parameter JSON format + +```json +"fqdns": { + // Example to set FQDNs for the Private Link service + "value": [ + "pls01.azure.privatelinkservice", // FQDN 1 + "pls01-duplicate.azure.privatelinkserivce" // FQDN 2 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +fqdns: [ + // Example to set FQDNs for the Private Link service + 'pls01.azure.privatelinkservice' + 'pls01-duplicate.azure.privatelinkservice' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private link service. | +| `resourceGroupName` | string | The resource group the private link service was deployed into. | +| `resourceId` | string | The resource ID of the private link service. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module privateLinkServices './Microsoft.Network/privateLinkServices/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkServices' + params: { + // Required parameters + name: '<>-az-pls-min-001' + // Non-required parameters + ipConfigurations: [ + { + name: 'minpls01' + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009' + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls-min' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-pls-min-001" + }, + // Non-required parameters + "ipConfigurations": { + "value": [ + { + "name": "minpls01", + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls-min" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module privateLinkServices './Microsoft.Network/privateLinkServices/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkServices' + params: { + // Required parameters + name: '<>-az-pls-001' + // Non-required parameters + autoApproval: { + subscriptions: [ + '*' + ] + } + enableProxyProtocol: true + fqdns: [ + '<>.plsfqdn01.azure.privatelinkservice' + '<>.plsfqdn02.azure.privatelinkserivce' + ] + ipConfigurations: [ + { + name: 'pls01' + properties: { + primary: true + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009' + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls' + } + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + visibility: { + subscriptions: [ + '<>' + ] + } + } +} +``` + +
+

+ +

+ +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": "<>-az-pls-001" + }, + // Non-required parameters + "autoApproval": { + "value": { + "subscriptions": [ + "*" + ] + } + }, + "enableProxyProtocol": { + "value": true + }, + "fqdns": { + "value": [ + "<>.plsfqdn01.azure.privatelinkservice", + "<>.plsfqdn02.azure.privatelinkserivce" + ] + }, + "ipConfigurations": { + "value": [ + { + "name": "pls01", + "properties": { + "primary": true, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-009" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-pls-001/frontendIPConfigurations/frontend-pls" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "visibility": { + "value": { + "subscriptions": [ + "<>" + ] + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/privateLinkServices/version.json b/modules/Microsoft.Network/privateLinkServices/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/privateLinkServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..1a78beb --- /dev/null +++ b/modules/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,74 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource publicIpAddress 'Microsoft.Network/publicIPAddresses@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(publicIpAddress.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: publicIpAddress +}] diff --git a/modules/Microsoft.Network/publicIPAddresses/.test/parameters.json b/modules/Microsoft.Network/publicIPAddresses/.test/parameters.json new file mode 100644 index 0000000..9a95bc2 --- /dev/null +++ b/modules/Microsoft.Network/publicIPAddresses/.test/parameters.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pip-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "skuName": { + "value": "Standard" + }, + "publicIPAllocationMethod": { + "value": "Static" + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/publicIPAddresses/deploy.bicep b/modules/Microsoft.Network/publicIPAddresses/deploy.bicep new file mode 100644 index 0000000..32ce98b --- /dev/null +++ b/modules/Microsoft.Network/publicIPAddresses/deploy.bicep @@ -0,0 +1,199 @@ +@description('Required. The name of the Public IP Address.') +param name string + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param publicIPPrefixResourceId string = '' + +@description('Optional. The public IP address allocation method.') +@allowed([ + 'Dynamic' + 'Static' +]) +param publicIPAllocationMethod string = 'Dynamic' + +@description('Optional. Name of a public IP address SKU.') +@allowed([ + 'Basic' + 'Standard' +]) +param skuName string = 'Basic' + +@description('Optional. Tier of a public IP address SKU.') +@allowed([ + 'Global' + 'Regional' +]) +param skuTier string = 'Regional' + +@description('Optional. A list of availability zones denoting the IP allocated for the resource needs to come from.') +param zones array = [] + +@description('Optional. IP address version.') +@allowed([ + 'IPv4' + 'IPv6' +]) +param publicIPAddressVersion string = 'IPv4' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param diagnosticLogCategoriesToEnable array = [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var publicIPPrefix = { + id: publicIPPrefixResourceId +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource publicIpAddress 'Microsoft.Network/publicIPAddresses@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + tier: skuTier + } + zones: zones + properties: { + publicIPAddressVersion: publicIPAddressVersion + publicIPAllocationMethod: publicIPAllocationMethod + publicIPPrefix: !empty(publicIPPrefixResourceId) ? publicIPPrefix : null + idleTimeoutInMinutes: 4 + ipTags: [] + } +} + +resource publicIpAddress_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${publicIpAddress.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: publicIpAddress +} + +resource publicIpAddress_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: publicIpAddress +} + +module publicIpAddress_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PIPAddress-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: publicIpAddress.id + } +}] + +@description('The resource group the public IP address was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the public IP address.') +output name string = publicIpAddress.name + +@description('The resource ID of the public IP address.') +output resourceId string = publicIpAddress.id + +@description('The public IP address of the public IP address resource.') +output ipAddress string = publicIpAddress.properties.ipAddress + +@description('The location the resource was deployed into.') +output location string = publicIpAddress.location diff --git a/modules/Microsoft.Network/publicIPAddresses/readme.md b/modules/Microsoft.Network/publicIPAddresses/readme.md new file mode 100644 index 0000000..3d9e49d --- /dev/null +++ b/modules/Microsoft.Network/publicIPAddresses/readme.md @@ -0,0 +1,273 @@ +# Public IP Addresses `[Microsoft.Network/publicIPAddresses]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Public IP Address. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicIPAddressVersion` | string | `'IPv4'` | `[IPv4, IPv6]` | IP address version. | +| `publicIPAllocationMethod` | string | `'Dynamic'` | `[Dynamic, Static]` | The public IP address allocation method. | +| `publicIPPrefixResourceId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `roleAssignments` | array | `[]` | | 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'. | +| `skuName` | string | `'Basic'` | `[Basic, Standard]` | Name of a public IP address SKU. | +| `skuTier` | string | `'Regional'` | `[Global, Regional]` | Tier of a public IP address SKU. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `zones` | array | `[]` | | A list of availability zones denoting the IP allocated for the resource needs to come from. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `ipAddress` | string | The public IP address of the public IP address resource. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the public IP address. | +| `resourceGroupName` | string | The resource group the public IP address was deployed into. | +| `resourceId` | string | The resource ID of the public IP address. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module publicIPAddresses './Microsoft.Network/publicIPAddresses/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PublicIPAddresses' + params: { + // Required parameters + name: '<>-az-pip-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + publicIPAllocationMethod: 'Static' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-pip-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "publicIPAllocationMethod": { + "value": "Static" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuName": { + "value": "Standard" + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/publicIPAddresses/version.json b/modules/Microsoft.Network/publicIPAddresses/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/publicIPAddresses/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..8f688f7 --- /dev/null +++ b/modules/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 publicIpPrefix 'Microsoft.Network/publicIPPrefixes@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(publicIpPrefix.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: publicIpPrefix +}] diff --git a/modules/Microsoft.Network/publicIPPrefixes/.test/parameters.json b/modules/Microsoft.Network/publicIPPrefixes/.test/parameters.json new file mode 100644 index 0000000..4367694 --- /dev/null +++ b/modules/Microsoft.Network/publicIPPrefixes/.test/parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pippfx-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "prefixLength": { + "value": 28 + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/publicIPPrefixes/deploy.bicep b/modules/Microsoft.Network/publicIPPrefixes/deploy.bicep new file mode 100644 index 0000000..a010a0d --- /dev/null +++ b/modules/Microsoft.Network/publicIPPrefixes/deploy.bicep @@ -0,0 +1,87 @@ +@description('Required. Name of the Public IP Prefix.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Length of the Public IP Prefix.') +@minValue(28) +@maxValue(31) +param prefixLength int + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource publicIpPrefix 'Microsoft.Network/publicIPPrefixes@2021-08-01' = { + name: name + location: location + tags: tags + sku: { + name: 'Standard' + } + properties: { + publicIPAddressVersion: 'IPv4' + prefixLength: prefixLength + } +} + +resource publicIpPrefix_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${publicIpPrefix.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: publicIpPrefix +} + +module publicIpPrefix_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PIPPrefix-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: publicIpPrefix.id + } +}] + +@description('The resource ID of the public IP prefix.') +output resourceId string = publicIpPrefix.id + +@description('The resource group the public IP prefix was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the public IP prefix.') +output name string = publicIpPrefix.name + +@description('The location the resource was deployed into.') +output location string = publicIpPrefix.location diff --git a/modules/Microsoft.Network/publicIPPrefixes/readme.md b/modules/Microsoft.Network/publicIPPrefixes/readme.md new file mode 100644 index 0000000..1545c70 --- /dev/null +++ b/modules/Microsoft.Network/publicIPPrefixes/readme.md @@ -0,0 +1,224 @@ +# Public IP Prefixes `[Microsoft.Network/publicIPPrefixes]` + +This template deploys a public IP prefix. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/publicIPPrefixes` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPPrefixes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Public IP Prefix. | +| `prefixLength` | int | Length of the Public IP Prefix. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the public IP prefix. | +| `resourceGroupName` | string | The resource group the public IP prefix was deployed into. | +| `resourceId` | string | The resource ID of the public IP prefix. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module publicIPPrefixes './Microsoft.Network/publicIPPrefixes/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PublicIPPrefixes' + params: { + // Required parameters + name: '<>-az-pippfx-x-001' + prefixLength: 28 + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-pippfx-x-001" + }, + "prefixLength": { + "value": 28 + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/publicIPPrefixes/version.json b/modules/Microsoft.Network/publicIPPrefixes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/publicIPPrefixes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..aa889b4 --- /dev/null +++ b/modules/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource routeTable 'Microsoft.Network/routeTables@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(routeTable.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: routeTable +}] diff --git a/modules/Microsoft.Network/routeTables/.test/parameters.json b/modules/Microsoft.Network/routeTables/.test/parameters.json new file mode 100644 index 0000000..65fa5d2 --- /dev/null +++ b/modules/Microsoft.Network/routeTables/.test/parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-udr-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "routes": { + "value": [ + { + "name": "default", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "VirtualAppliance", + "nextHopIpAddress": "172.16.0.20" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/routeTables/deploy.bicep b/modules/Microsoft.Network/routeTables/deploy.bicep new file mode 100644 index 0000000..2fd2a0f --- /dev/null +++ b/modules/Microsoft.Network/routeTables/deploy.bicep @@ -0,0 +1,84 @@ +@description('Required. Name given for the hub route table.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. An Array of Routes to be established within the hub route table.') +param routes array = [] + +@description('Optional. Switch to disable BGP route propagation.') +param disableBgpRoutePropagation bool = false + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource routeTable 'Microsoft.Network/routeTables@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + routes: routes + disableBgpRoutePropagation: disableBgpRoutePropagation + } +} + +resource routeTable_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${routeTable.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: routeTable +} + +module routeTable_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-RouteTable-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: routeTable.id + } +}] + +@description('The resource group the route table was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the route table.') +output name string = routeTable.name + +@description('The resource ID of the route table.') +output resourceId string = routeTable.id + +@description('The location the resource was deployed into.') +output location string = routeTable.location diff --git a/modules/Microsoft.Network/routeTables/readme.md b/modules/Microsoft.Network/routeTables/readme.md new file mode 100644 index 0000000..f5ca069 --- /dev/null +++ b/modules/Microsoft.Network/routeTables/readme.md @@ -0,0 +1,332 @@ +# Route Tables `[Microsoft.Network/routeTables]` + +This module deploys a user defined route table. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/routeTables` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/routeTables) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name given for the hub route table. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `disableBgpRoutePropagation` | bool | `False` | | Switch to disable BGP route propagation. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `routes` | array | `[]` | | An Array of Routes to be established within the hub route table. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `routes` + +The `routes` parameter accepts a JSON Array of Route objects to deploy to the Route Table. + +Here's an example of specifying a few routes: + +

+ +Parameter JSON format + +```json +"routes": { + "value": [ + { + "name": "tojumpboxes", + "properties": { + "addressPrefix": "172.16.0.48/28", + "nextHopType": "VnetLocal" + } + }, + { + "name": "tosharedservices", + "properties": { + "addressPrefix": "172.16.0.64/27", + "nextHopType": "VnetLocal" + } + }, + { + "name": "toonprem", + "properties": { + "addressPrefix": "10.0.0.0/8", + "nextHopType": "VirtualNetworkGateway" + } + }, + { + "name": "tonva", + "properties": { + "addressPrefix": "172.16.0.0/18", + "nextHopType": "VirtualAppliance", + "nextHopIpAddress": "172.16.0.20" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +routes: [ + { + name: 'tojumpboxes' + properties: { + addressPrefix: '172.16.0.48/28' + nextHopType: 'VnetLocal' + } + } + { + name: 'tosharedservices' + properties: { + addressPrefix: '172.16.0.64/27' + nextHopType: 'VnetLocal' + } + } + { + name: 'toonprem' + properties: { + addressPrefix: '10.0.0.0/8' + nextHopType: 'VirtualNetworkGateway' + } + } + { + name: 'tonva' + properties: { + addressPrefix: '172.16.0.0/18' + nextHopType: 'VirtualAppliance' + nextHopIpAddress: '172.16.0.20' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the route table. | +| `resourceGroupName` | string | The resource group the route table was deployed into. | +| `resourceId` | string | The resource ID of the route table. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module routeTables './Microsoft.Network/routeTables/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-RouteTables' + params: { + // Required parameters + name: '<>-az-udr-x-001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + routes: [ + { + name: 'default' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopIpAddress: '172.16.0.20' + nextHopType: 'VirtualAppliance' + } + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-udr-x-001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "routes": { + "value": [ + { + "name": "default", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopIpAddress": "172.16.0.20", + "nextHopType": "VirtualAppliance" + } + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/routeTables/version.json b/modules/Microsoft.Network/routeTables/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/routeTables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..edfb5b1 --- /dev/null +++ b/modules/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource trafficmanagerprofile 'Microsoft.Network/trafficmanagerprofiles@2018-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(trafficmanagerprofile.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: trafficmanagerprofile +}] diff --git a/modules/Microsoft.Network/trafficmanagerprofiles/.test/parameters.json b/modules/Microsoft.Network/trafficmanagerprofiles/.test/parameters.json new file mode 100644 index 0000000..220f646 --- /dev/null +++ b/modules/Microsoft.Network/trafficmanagerprofiles/.test/parameters.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "tm-000001" + }, + "lock": { + "value": "CanNotDelete" + }, + "relativeName": { + "value": "tm-000001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/trafficmanagerprofiles/deploy.bicep b/modules/Microsoft.Network/trafficmanagerprofiles/deploy.bicep new file mode 100644 index 0000000..05bb124 --- /dev/null +++ b/modules/Microsoft.Network/trafficmanagerprofiles/deploy.bicep @@ -0,0 +1,193 @@ +@description('Required. Name of the Traffic Manager.') +@minLength(1) +param name string + +@description('Optional. The status of the Traffic Manager profile.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param profileStatus string = 'Enabled' + +@description('Optional. The traffic routing method of the Traffic Manager profile.') +@allowed([ + 'Performance' + 'Priority' + 'Weighted' + 'Geographic' + 'MultiValue' + 'Subnet' +]) +param trafficRoutingMethod string = 'Performance' + +@description('Required. The relative DNS name provided by this Traffic Manager profile. This value is combined with the DNS domain name used by Azure Traffic Manager to form the fully-qualified domain name (FQDN) of the profile.') +param relativeName string + +@description('Optional. The DNS Time-To-Live (TTL), in seconds. This informs the local DNS resolvers and DNS clients how long to cache DNS responses provided by this Traffic Manager profile.') +param ttl int = 60 + +@description('Optional. The endpoint monitoring settings of the Traffic Manager profile.') +param monitorConfig object = { + protocol: 'http' + port: '80' + path: '/' +} + +@description('Optional. The list of endpoints in the Traffic Manager profile.') +param endpoints array = [] + +@description('Optional. Indicates whether Traffic View is \'Enabled\' or \'Disabled\' for the Traffic Manager profile. Null, indicates \'Disabled\'. Enabling this feature will increase the cost of the Traffic Manage profile.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param trafficViewEnrollmentStatus string = 'Disabled' + +@description('Optional. Maximum number of endpoints to be returned for MultiValue routing type.') +param maxReturn int = 1 + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ProbeHealthStatusEvents' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ProbeHealthStatusEvents' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource trafficManagerProfile 'Microsoft.Network/trafficmanagerprofiles@2018-08-01' = { + name: name + tags: tags + location: 'global' + properties: { + profileStatus: profileStatus + trafficRoutingMethod: trafficRoutingMethod + dnsConfig: { + relativeName: relativeName + ttl: ttl + } + monitorConfig: monitorConfig + endpoints: endpoints + trafficViewEnrollmentStatus: trafficViewEnrollmentStatus + maxReturn: maxReturn + } +} + +resource trafficManagerProfile_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${trafficManagerProfile.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: trafficManagerProfile +} + +resource trafficManagerProfile_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: trafficManagerProfile +} + +module trafficManagerProfile_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-TrafficManagerProfile-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: trafficManagerProfile.id + } +}] + +@description('The resource ID of the traffic manager.') +output resourceId string = trafficManagerProfile.id + +@description('The resource group the traffic manager was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the traffic manager was deployed into.') +output name string = trafficManagerProfile.name diff --git a/modules/Microsoft.Network/trafficmanagerprofiles/readme.md b/modules/Microsoft.Network/trafficmanagerprofiles/readme.md new file mode 100644 index 0000000..93ab65d --- /dev/null +++ b/modules/Microsoft.Network/trafficmanagerprofiles/readme.md @@ -0,0 +1,348 @@ +# Traffic Manager Profiles `[Microsoft.Network/trafficmanagerprofiles]` + +This module deploys a traffic manager profile. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/trafficmanagerprofiles` | [2018-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2018-08-01/trafficmanagerprofiles) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Traffic Manager. | +| `relativeName` | string | The relative DNS name provided by this Traffic Manager profile. This value is combined with the DNS domain name used by Azure Traffic Manager to form the fully-qualified domain name (FQDN) of the profile. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ProbeHealthStatusEvents]` | `[ProbeHealthStatusEvents]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `endpoints` | array | `[]` | | The list of endpoints in the Traffic Manager profile. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxReturn` | int | `1` | | Maximum number of endpoints to be returned for MultiValue routing type. | +| `monitorConfig` | object | `{object}` | | The endpoint monitoring settings of the Traffic Manager profile. | +| `profileStatus` | string | `'Enabled'` | `[Disabled, Enabled]` | The status of the Traffic Manager profile. | +| `roleAssignments` | array | `[]` | | 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}` | | Resource tags. | +| `trafficRoutingMethod` | string | `'Performance'` | `[Geographic, MultiValue, Performance, Priority, Subnet, Weighted]` | The traffic routing method of the Traffic Manager profile. | +| `trafficViewEnrollmentStatus` | string | `'Disabled'` | `[Disabled, Enabled]` | Indicates whether Traffic View is 'Enabled' or 'Disabled' for the Traffic Manager profile. Null, indicates 'Disabled'. Enabling this feature will increase the cost of the Traffic Manage profile. | +| `ttl` | int | `60` | | The DNS Time-To-Live (TTL), in seconds. This informs the local DNS resolvers and DNS clients how long to cache DNS responses provided by this Traffic Manager profile. | + + +### Parameter Usage: `monitorConfig` + +

+ +Parameter JSON format + +```json +"monitorConfig": { + "value": { + "protocol": "http", + "port": "80", + "path": "/" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +monitorConfig: { + protocol: 'http' + port: '80' + path: '/' +} +``` + +
+

+ +### Parameter Usage: `endpoints` + +

+ +Parameter JSON format + +```json +"endpoints": { + "value": [ + { + "id": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/trafficManagerProfiles//azureEndpoints/", + "name": "MyEndpoint001", + "type": "Microsoft.Network/trafficManagerProfiles/azureEndpoints", + "properties": + { + "endpointStatus": "Enabled", + "endpointMonitorStatus": "CheckingEndpoint", + "targetResourceId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/publicIPAddresses/", + "target": "my-pip-001.eastus.cloudapp.azure.com", + "weight": 1, + "priority": 1, + "endpointLocation": "East US" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +endpoints: [ + { + id: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/trafficManagerProfiles//azureEndpoints/' + name: 'MyEndpoint001' + type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints' + properties: + { + endpointStatus: 'Enabled' + endpointMonitorStatus: 'CheckingEndpoint' + targetResourceId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/publicIPAddresses/' + target: 'my-pip-001.eastus.cloudapp.azure.com' + weight: 1 + priority: 1 + endpointLocation: 'East US' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the traffic manager was deployed into. | +| `resourceGroupName` | string | The resource group the traffic manager was deployed into. | +| `resourceId` | string | The resource ID of the traffic manager. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module trafficmanagerprofiles './Microsoft.Network/trafficmanagerprofiles/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Trafficmanagerprofiles' + params: { + // Required parameters + name: 'tm-000001' + relativeName: 'tm-000001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "tm-000001" + }, + "relativeName": { + "value": "tm-000001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/trafficmanagerprofiles/version.json b/modules/Microsoft.Network/trafficmanagerprofiles/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/trafficmanagerprofiles/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualHubs/.test/min.parameters.json b/modules/Microsoft.Network/virtualHubs/.test/min.parameters.json new file mode 100644 index 0000000..dbe2fa6 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/.test/min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vhub-min-001" + }, + "addressPrefix": { + "value": "10.0.0.0/16" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001" + } + } +} diff --git a/modules/Microsoft.Network/virtualHubs/.test/parameters.json b/modules/Microsoft.Network/virtualHubs/.test/parameters.json new file mode 100644 index 0000000..2660f1b --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/.test/parameters.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vhub-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "addressPrefix": { + "value": "10.1.0.0/16" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001" + }, + "hubRouteTables": { + "value": [ + { + "name": "routeTable1" + } + ] + }, + "hubVirtualNetworkConnections": { + "value": [ + { + "name": "connection1", + "remoteVirtualNetworkId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-vhub", + "routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1" + }, + "propagatedRouteTables": { + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1" + } + ], + "labels": [ + "none" + ] + } + } + } + ] + } + } +} diff --git a/modules/Microsoft.Network/virtualHubs/deploy.bicep b/modules/Microsoft.Network/virtualHubs/deploy.bicep new file mode 100644 index 0000000..34145e5 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/deploy.bicep @@ -0,0 +1,178 @@ +@description('Required. The virtual hub name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Required. Address-prefix for this VirtualHub.') +param addressPrefix string + +@description('Optional. Flag to control transit for VirtualRouter hub.') +param allowBranchToBranchTraffic bool = true + +@description('Optional. Resource ID of the Azure Firewall to link to.') +param azureFirewallId string = '' + +@description('Optional. Resource ID of the Express Route Gateway to link to.') +param expressRouteGatewayId string = '' + +@description('Optional. Resource ID of the Point-to-Site VPN Gateway to link to.') +param p2SVpnGatewayId string = '' + +@description('Optional. The preferred routing gateway types.') +@allowed([ + 'ExpressRoute' + 'None' + 'VpnGateway' + '' +]) +param preferredRoutingGateway string = '' + +@description('Optional. VirtualHub route tables.') +param routeTableRoutes array = [] + +@description('Optional. ID of the Security Partner Provider to link to.') +param securityPartnerProviderId string = '' + +@description('Optional. The Security Provider name.') +param securityProviderName string = '' + +@allowed([ + 'Basic' + 'Standard' +]) +@description('Optional. The sku of this VirtualHub.') +param sku string = 'Standard' + +@description('Optional. List of all virtual hub route table v2s associated with this VirtualHub.') +param virtualHubRouteTableV2s array = [] + +@description('Optional. VirtualRouter ASN.') +param virtualRouterAsn int = -1 + +@description('Optional. VirtualRouter IPs.') +param virtualRouterIps array = [] + +@description('Required. Resource ID of the virtual WAN to link to.') +param virtualWanId string + +@description('Optional. Resource ID of the VPN Gateway to link to.') +param vpnGatewayId string = '' + +@description('Optional. Route tables to create for the virtual hub.') +param hubRouteTables array = [] + +@description('Optional. Virtual network connections to create for the virtual hub.') +param hubVirtualNetworkConnections array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + addressPrefix: addressPrefix + allowBranchToBranchTraffic: allowBranchToBranchTraffic + azureFirewall: !empty(azureFirewallId) ? { + id: azureFirewallId + } : null + expressRouteGateway: !empty(expressRouteGatewayId) ? { + id: expressRouteGatewayId + } : null + p2SVpnGateway: !empty(p2SVpnGatewayId) ? { + id: p2SVpnGatewayId + } : null + preferredRoutingGateway: !empty(preferredRoutingGateway) ? any(preferredRoutingGateway) : null + routeTable: !empty(routeTableRoutes) ? { + routes: routeTableRoutes + } : null + securityPartnerProvider: !empty(securityPartnerProviderId) ? { + id: securityPartnerProviderId + } : null + securityProviderName: securityProviderName + sku: sku + virtualHubRouteTableV2s: virtualHubRouteTableV2s + virtualRouterAsn: virtualRouterAsn != -1 ? virtualRouterAsn : null + virtualRouterIps: !empty(virtualRouterIps) ? virtualRouterIps : null + virtualWan: { + id: virtualWanId + } + vpnGateway: !empty(vpnGatewayId) ? { + id: vpnGatewayId + } : null + } +} + +resource virtualHub_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${virtualHub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualHub +} + +module virtualHub_routeTables 'hubRouteTables/deploy.bicep' = [for (routeTable, index) in hubRouteTables: { + name: '${uniqueString(deployment().name, location)}-routeTable-${index}' + params: { + virtualHubName: virtualHub.name + name: routeTable.name + labels: contains(routeTable, 'labels') ? routeTable.labels : [] + routes: contains(routeTable, 'routes') ? routeTable.routes : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module virtualHub_hubVirtualNetworkConnections 'hubVirtualNetworkConnections/deploy.bicep' = [for (virtualNetworkConnection, index) in hubVirtualNetworkConnections: { + name: '${uniqueString(deployment().name, location)}-connection-${index}' + params: { + virtualHubName: virtualHub.name + name: virtualNetworkConnection.name + enableInternetSecurity: contains(virtualNetworkConnection, 'enableInternetSecurity') ? virtualNetworkConnection.enableInternetSecurity : true + remoteVirtualNetworkId: virtualNetworkConnection.remoteVirtualNetworkId + routingConfiguration: contains(virtualNetworkConnection, 'routingConfiguration') ? virtualNetworkConnection.routingConfiguration : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + virtualHub_routeTables + ] +}] + +@description('The resource group the virtual hub was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual hub.') +output resourceId string = virtualHub.id + +@description('The name of the virtual hub.') +output name string = virtualHub.name + +@description('The location the resource was deployed into.') +output location string = virtualHub.location diff --git a/modules/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep b/modules/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep new file mode 100644 index 0000000..a225e20 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep @@ -0,0 +1,48 @@ +@description('Required. The route table name.') +param name string + +@description('Conditional. The name of the parent virtual hub. Required if the template is used in a standalone deployment.') +param virtualHubName string + +@description('Optional. List of labels associated with this route table.') +param labels array = [] + +@description('Optional. List of all routes.') +param routes array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2021-08-01' existing = { + name: virtualHubName +} + +resource hubRouteTable 'Microsoft.Network/virtualHubs/hubRouteTables@2021-08-01' = { + name: name + parent: virtualHub + properties: { + labels: !empty(labels) ? labels : null + routes: !empty(routes) ? routes : null + } +} + +@description('The name of the deployed virtual hub route table.') +output name string = hubRouteTable.name + +@description('The resource ID of the deployed virtual hub route table.') +output resourceId string = hubRouteTable.id + +@description('The resource group the virtual hub route table was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/virtualHubs/hubRouteTables/readme.md b/modules/Microsoft.Network/virtualHubs/hubRouteTables/readme.md new file mode 100644 index 0000000..5801b2c --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubRouteTables/readme.md @@ -0,0 +1,48 @@ +# Virtual Hub Route Table `[Microsoft.Network/virtualHubs/hubRouteTables]` + +This module deploys virtual hub route tables. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualHubs/hubRouteTables` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualHubs/hubRouteTables) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The route table name. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualHubName` | string | The name of the parent virtual hub. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `labels` | array | `[]` | List of labels associated with this route table. | +| `routes` | array | `[]` | List of all routes. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed virtual hub route table. | +| `resourceGroupName` | string | The resource group the virtual hub route table was deployed into. | +| `resourceId` | string | The resource ID of the deployed virtual hub route table. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/virtualHubs/hubRouteTables/version.json b/modules/Microsoft.Network/virtualHubs/hubRouteTables/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubRouteTables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep new file mode 100644 index 0000000..6c41249 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep @@ -0,0 +1,54 @@ +@description('Required. The connection name.') +param name string + +@description('Conditional. The name of the parent virtual hub. Required if the template is used in a standalone deployment.') +param virtualHubName string + +@description('Optional. Enable internet security.') +param enableInternetSecurity bool = true + +@description('Required. Resource ID of the virtual network to link to.') +param remoteVirtualNetworkId string + +@description('Optional. Routing Configuration indicating the associated and propagated route tables for this connection.') +param routingConfiguration object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2021-08-01' existing = { + name: virtualHubName +} + +resource hubVirtualNetworkConnection 'Microsoft.Network/virtualHubs/hubVirtualNetworkConnections@2021-08-01' = { + name: name + parent: virtualHub + properties: { + enableInternetSecurity: enableInternetSecurity + remoteVirtualNetwork: { + id: remoteVirtualNetworkId + } + routingConfiguration: !empty(routingConfiguration) ? routingConfiguration : null + } +} + +@description('The resource group the virtual hub connection was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual hub connection.') +output resourceId string = hubVirtualNetworkConnection.id + +@description('The name of the virtual hub connection.') +output name string = hubVirtualNetworkConnection.name diff --git a/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md new file mode 100644 index 0000000..cb1a741 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md @@ -0,0 +1,53 @@ +# Virtual Hub Virtual Network Connections `[Microsoft.Network/virtualHubs/hubVirtualNetworkConnections]` + +This module deploys virtual hub virtual network connections. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualHubs/hubVirtualNetworkConnections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The connection name. | +| `remoteVirtualNetworkId` | string | Resource ID of the virtual network to link to. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualHubName` | string | The name of the parent virtual hub. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableInternetSecurity` | bool | `True` | Enable internet security. | +| `routingConfiguration` | object | `{object}` | Routing Configuration indicating the associated and propagated route tables for this connection. | + + +### Parameter Usage: `hubVirtualNetworkConnections` + +... + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual hub connection. | +| `resourceGroupName` | string | The resource group the virtual hub connection was deployed into. | +| `resourceId` | string | The resource ID of the virtual hub connection. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualHubs/readme.md b/modules/Microsoft.Network/virtualHubs/readme.md new file mode 100644 index 0000000..b6caea6 --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/readme.md @@ -0,0 +1,270 @@ +# Virtual Hubs `[Microsoft.Network/virtualHubs]` + +This module deploys a Virtual Hub. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Network/virtualHubs` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualHubs) | +| `Microsoft.Network/virtualHubs/hubRouteTables` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualHubs/hubRouteTables) | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualHubs/hubVirtualNetworkConnections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefix` | string | Address-prefix for this VirtualHub. | +| `name` | string | The virtual hub name. | +| `virtualWanId` | string | Resource ID of the virtual WAN to link to. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowBranchToBranchTraffic` | bool | `True` | | Flag to control transit for VirtualRouter hub. | +| `azureFirewallId` | string | `''` | | Resource ID of the Azure Firewall to link to. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `expressRouteGatewayId` | string | `''` | | Resource ID of the Express Route Gateway to link to. | +| `hubRouteTables` | _[hubRouteTables](hubRouteTables/readme.md)_ array | `[]` | | Route tables to create for the virtual hub. | +| `hubVirtualNetworkConnections` | _[hubVirtualNetworkConnections](hubVirtualNetworkConnections/readme.md)_ array | `[]` | | Virtual network connections to create for the virtual hub. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `p2SVpnGatewayId` | string | `''` | | Resource ID of the Point-to-Site VPN Gateway to link to. | +| `preferredRoutingGateway` | string | `''` | `['', ExpressRoute, None, VpnGateway]` | The preferred routing gateway types. | +| `routeTableRoutes` | array | `[]` | | VirtualHub route tables. | +| `securityPartnerProviderId` | string | `''` | | ID of the Security Partner Provider to link to. | +| `securityProviderName` | string | `''` | | The Security Provider name. | +| `sku` | string | `'Standard'` | `[Basic, Standard]` | The sku of this VirtualHub. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `virtualHubRouteTableV2s` | array | `[]` | | List of all virtual hub route table v2s associated with this VirtualHub. | +| `virtualRouterAsn` | int | `-1` | | VirtualRouter ASN. | +| `virtualRouterIps` | array | `[]` | | VirtualRouter IPs. | +| `vpnGatewayId` | string | `''` | | Resource ID of the VPN Gateway to link to. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual hub. | +| `resourceGroupName` | string | The resource group the virtual hub was deployed into. | +| `resourceId` | string | The resource ID of the virtual hub. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module virtualHubs './Microsoft.Network/virtualHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualHubs' + params: { + // Required parameters + addressPrefix: '10.0.0.0/16' + name: '<>-az-vhub-min-001' + virtualWanId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001' + } +} +``` + +
+

+ +

+ +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 + "addressPrefix": { + "value": "10.0.0.0/16" + }, + "name": { + "value": "<>-az-vhub-min-001" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module virtualHubs './Microsoft.Network/virtualHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualHubs' + params: { + // Required parameters + addressPrefix: '10.1.0.0/16' + name: '<>-az-vhub-x-001' + virtualWanId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001' + // Non-required parameters + hubRouteTables: [ + { + name: 'routeTable1' + } + ] + hubVirtualNetworkConnections: [ + { + name: 'connection1' + remoteVirtualNetworkId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-vhub' + routingConfiguration: { + associatedRouteTable: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1' + } + propagatedRouteTables: { + ids: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1' + } + ] + labels: [ + 'none' + ] + } + } + } + ] + lock: 'CanNotDelete' + } +} +``` + +
+

+ +

+ +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 + "addressPrefix": { + "value": "10.1.0.0/16" + }, + "name": { + "value": "<>-az-vhub-x-001" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/adp-<>-az-vw-x-001" + }, + // Non-required parameters + "hubRouteTables": { + "value": [ + { + "name": "routeTable1" + } + ] + }, + "hubVirtualNetworkConnections": { + "value": [ + { + "name": "connection1", + "remoteVirtualNetworkId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-vhub", + "routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1" + }, + "propagatedRouteTables": { + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vHub-x-001/hubRouteTables/routeTable1" + } + ], + "labels": [ + "none" + ] + } + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/virtualHubs/version.json b/modules/Microsoft.Network/virtualHubs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualHubs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..972012d --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualNetworkGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualNetworkGateway +}] diff --git a/modules/Microsoft.Network/virtualNetworkGateways/.test/expressRoute.parameters.json b/modules/Microsoft.Network/virtualNetworkGateways/.test/expressRoute.parameters.json new file mode 100644 index 0000000..3de5a1f --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/.test/expressRoute.parameters.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-gw-er-001" + }, + "gatewayPipName": { + "value": "<>-az-gw-er-001-pip" + }, + "domainNameLabel": { + "value": [ + "<>-az-gw-er-dm-001" + ] + }, + "virtualNetworkGatewayType": { + "value": "ExpressRoute" + }, + "virtualNetworkGatewaySku": { + "value": "ErGw1AZ" + }, + "vNetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + "tags": { + "value": { + "Environment": "Validation", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "", + "CostCenter": "", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/virtualNetworkGateways/.test/vpn.parameters.json b/modules/Microsoft.Network/virtualNetworkGateways/.test/vpn.parameters.json new file mode 100644 index 0000000..cf037dc --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/.test/vpn.parameters.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-gw-vpn-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "domainNameLabel": { + "value": [ + "<>-az-gw-vpn-dm-001" + ] + }, + "virtualNetworkGatewayType": { + "value": "Vpn" + }, + "virtualNetworkGatewaySku": { + "value": "VpnGw1AZ" + }, + "publicIpZones": { + "value": [ + "1" + ] + }, + "vpnType": { + "value": "RouteBased" + }, + "activeActive": { + "value": true + }, + "vNetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Network/virtualNetworkGateways/deploy.bicep b/modules/Microsoft.Network/virtualNetworkGateways/deploy.bicep new file mode 100644 index 0000000..e032f02 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/deploy.bicep @@ -0,0 +1,413 @@ +@description('Required. Specifies the Virtual Network Gateway name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the name of the Public IP used by the Virtual Network Gateway. If it\'s not provided, a \'-pip\' suffix will be appended to the gateway\'s name.') +param gatewayPipName string = '${name}-pip1' + +@description('Optional. Specifies the name of the Public IP used by the Virtual Network Gateway when active-active configuration is required. If it\'s not provided, a \'-pip\' suffix will be appended to the gateway\'s name.') +param activeGatewayPipName string = '${name}-pip2' + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param publicIPPrefixResourceId string = '' + +@description('Optional. Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones.') +param publicIpZones array = [] + +@description('Optional. DNS name(s) of the Public IP resource(s). If you enabled active-active configuration, you need to provide 2 DNS names, if you want to use this feature. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com.') +param domainNameLabel array = [] + +@description('Required. Specifies the gateway type. E.g. VPN, ExpressRoute.') +@allowed([ + 'Vpn' + 'ExpressRoute' +]) +param virtualNetworkGatewayType string + +@description('Required. The SKU of the Gateway.') +@allowed([ + 'Basic' + 'VpnGw1' + 'VpnGw2' + 'VpnGw3' + 'VpnGw1AZ' + 'VpnGw2AZ' + 'VpnGw3AZ' + 'Standard' + 'HighPerformance' + 'UltraPerformance' + 'ErGw1AZ' + 'ErGw2AZ' + 'ErGw3AZ' +]) +param virtualNetworkGatewaySku string + +@description('Optional. Specifies the VPN type.') +@allowed([ + 'PolicyBased' + 'RouteBased' +]) +param vpnType string = 'RouteBased' + +@description('Required. Virtual Network resource ID.') +param vNetResourceId string + +@description('Optional. Value to specify if the Gateway should be deployed in active-active or active-passive configuration.') +param activeActive bool = true + +@description('Optional. Value to specify if BGP is enabled or not.') +param enableBgp bool = true + +@description('Optional. ASN value.') +param asn int = 65815 + +@description('Optional. The IP address range from which VPN clients will receive an IP address when connected. Range specified must not overlap with on-premise network.') +param vpnClientAddressPoolPrefix string = '' + +@description('Optional. Client root certificate data used to authenticate VPN clients.') +param clientRootCertData string = '' + +@description('Optional. Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet.') +param clientRevokedCertThumbprint string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param publicIpdiagnosticLogCategoriesToEnable array = [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +] + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'GatewayDiagnosticLog' + 'TunnelDiagnosticLog' + 'RouteDiagnosticLog' + 'IKEDiagnosticLog' + 'P2SDiagnosticLog' +]) +param virtualNetworkGatewaydiagnosticLogCategoriesToEnable array = [ + 'GatewayDiagnosticLog' + 'TunnelDiagnosticLog' + 'RouteDiagnosticLog' + 'IKEDiagnosticLog' + 'P2SDiagnosticLog' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param virtualNetworkGatewayDiagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. The name of the diagnostic setting, if deployed.') +param publicIpDiagnosticSettingsName string = 'diagnosticSettings' + +// ================// +// Variables // +// ================// + +// Diagnostic Variables +var virtualNetworkGatewayDiagnosticsLogs = [for category in virtualNetworkGatewaydiagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var publicIpDiagnosticsLogs = [for category in publicIpdiagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +// Other Variables +var zoneRedundantSkus = [ + 'VpnGw1AZ' + 'VpnGw2AZ' + 'VpnGw3AZ' + 'VpnGw4AZ' + 'VpnGw5AZ' + 'ErGw1AZ' + 'ErGw2AZ' + 'ErGw3AZ' +] +var gatewayPipSku = contains(zoneRedundantSkus, virtualNetworkGatewaySku) ? 'Standard' : 'Basic' +var gatewayPipAllocationMethod = contains(zoneRedundantSkus, virtualNetworkGatewaySku) ? 'Static' : 'Dynamic' + +var isActiveActiveValid = virtualNetworkGatewayType != 'ExpressRoute' ? activeActive : false +var virtualGatewayPipName_var = isActiveActiveValid ? [ + gatewayPipName + activeGatewayPipName +] : [ + gatewayPipName +] + +var vpnType_var = virtualNetworkGatewayType != 'ExpressRoute' ? vpnType : 'PolicyBased' + +var isBgpValid = virtualNetworkGatewayType != 'ExpressRoute' ? enableBgp : false +var bgpSettings = { + asn: asn +} + +// Potential configurations (active-active vs active-passive) +var ipConfiguration = isActiveActiveValid ? [ + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig1' + } + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: isActiveActiveValid ? az.resourceId('Microsoft.Network/publicIPAddresses', activeGatewayPipName) : az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig2' + } +] : [ + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig1' + } +] + +var vpnClientConfiguration = { + vpnClientAddressPool: { + addressPrefixes: [ + vpnClientAddressPoolPrefix + ] + } + vpnClientRootCertificates: !empty(clientRootCertData) ? [ + { + name: 'RootCert1' + properties: { + PublicCertData: clientRootCertData + } + } + ] : null + vpnClientRevokedCertificates: !empty(clientRevokedCertThumbprint) ? [ + { + name: 'RevokedCert1' + properties: { + Thumbprint: clientRevokedCertThumbprint + } + } + ] : null +} + +// ================// +// Deployments // +// ================// +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// Public IPs +@batchSize(1) +resource virtualGatewayPublicIP 'Microsoft.Network/publicIPAddresses@2021-08-01' = [for (virtualGatewayPublicIpName, index) in virtualGatewayPipName_var: { + name: virtualGatewayPublicIpName + location: location + tags: tags + sku: { + name: gatewayPipSku + } + properties: { + publicIPAllocationMethod: gatewayPipAllocationMethod + publicIPPrefix: !empty(publicIPPrefixResourceId) ? { + id: publicIPPrefixResourceId + } : null + dnsSettings: length(virtualGatewayPipName_var) == length(domainNameLabel) ? { + domainNameLabel: domainNameLabel[index] + } : null + } + zones: contains(zoneRedundantSkus, virtualNetworkGatewaySku) ? publicIpZones : null +}] + +@batchSize(1) +resource virtualGatewayPublicIP_lock 'Microsoft.Authorization/locks@2017-04-01' = [for (virtualGatewayPublicIpName, index) in virtualGatewayPipName_var: if (!empty(lock)) { + name: '${virtualGatewayPublicIpName}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualGatewayPublicIP[index] +}] + +@batchSize(1) +resource virtualNetworkGatewayPublicIp_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = [for (virtualGatewayPublicIpName, index) in virtualGatewayPipName_var: if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: '${virtualGatewayPublicIP[index].name}-${publicIpDiagnosticSettingsName}' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: publicIpDiagnosticsLogs + } + scope: virtualGatewayPublicIP[index] +}] + +// VNET Gateway +// ============ +resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + ipConfigurations: ipConfiguration + activeActive: isActiveActiveValid + enableBgp: isBgpValid + bgpSettings: isBgpValid ? bgpSettings : null + sku: { + name: virtualNetworkGatewaySku + tier: virtualNetworkGatewaySku + } + gatewayType: virtualNetworkGatewayType + vpnType: vpnType_var + vpnClientConfiguration: !empty(vpnClientAddressPoolPrefix) ? vpnClientConfiguration : null + } + dependsOn: [ + virtualGatewayPublicIP + ] +} + +resource virtualNetworkGateway_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${virtualNetworkGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualNetworkGateway +} + +resource virtualNetworkGateway_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: virtualNetworkGatewayDiagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: virtualNetworkGatewayDiagnosticsLogs + } + scope: virtualNetworkGateway +} + +module virtualNetworkGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VNetGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualNetworkGateway.id + } +}] + +// ================// +// Outputs // +// ================// +@description('The resource group the virtual network gateway was deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network gateway.') +output name string = virtualNetworkGateway.name + +@description('The resource ID of the virtual network gateway.') +output resourceId string = virtualNetworkGateway.id + +@description('Shows if the virtual network gateway is configured in active-active mode.') +output activeActive bool = virtualNetworkGateway.properties.activeActive + +@description('The location the resource was deployed into.') +output location string = virtualNetworkGateway.location diff --git a/modules/Microsoft.Network/virtualNetworkGateways/readme.md b/modules/Microsoft.Network/virtualNetworkGateways/readme.md new file mode 100644 index 0000000..7d631c9 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/readme.md @@ -0,0 +1,491 @@ +# Virtual Network Gateways `[Microsoft.Network/virtualNetworkGateways]` + +This module deploys a virtual network gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/publicIPAddresses` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/publicIPAddresses) | +| `Microsoft.Network/virtualNetworkGateways` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworkGateways) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Specifies the Virtual Network Gateway name. | +| `virtualNetworkGatewaySku` | string | `[Basic, ErGw1AZ, ErGw2AZ, ErGw3AZ, HighPerformance, Standard, UltraPerformance, VpnGw1, VpnGw1AZ, VpnGw2, VpnGw2AZ, VpnGw3, VpnGw3AZ]` | The SKU of the Gateway. | +| `virtualNetworkGatewayType` | string | `[ExpressRoute, Vpn]` | Specifies the gateway type. E.g. VPN, ExpressRoute. | +| `vNetResourceId` | string | | Virtual Network resource ID. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `activeActive` | bool | `True` | | Value to specify if the Gateway should be deployed in active-active or active-passive configuration. | +| `activeGatewayPipName` | string | `[format('{0}-pip2', parameters('name'))]` | | Specifies the name of the Public IP used by the Virtual Network Gateway when active-active configuration is required. If it's not provided, a '-pip' suffix will be appended to the gateway's name. | +| `asn` | int | `65815` | | ASN value. | +| `clientRevokedCertThumbprint` | string | `''` | | Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet. | +| `clientRootCertData` | string | `''` | | Client root certificate data used to authenticate VPN clients. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `domainNameLabel` | array | `[]` | | DNS name(s) of the Public IP resource(s). If you enabled active-active configuration, you need to provide 2 DNS names, if you want to use this feature. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com. | +| `enableBgp` | bool | `True` | | Value to specify if BGP is enabled or not. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `gatewayPipName` | string | `[format('{0}-pip1', parameters('name'))]` | | Specifies the name of the Public IP used by the Virtual Network Gateway. If it's not provided, a '-pip' suffix will be appended to the gateway's name. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicIpdiagnosticLogCategoriesToEnable` | array | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | `[DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. | +| `publicIpDiagnosticSettingsName` | string | `'diagnosticSettings'` | | The name of the diagnostic setting, if deployed. | +| `publicIPPrefixResourceId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `publicIpZones` | array | `[]` | | Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | +| `virtualNetworkGatewaydiagnosticLogCategoriesToEnable` | array | `[GatewayDiagnosticLog, IKEDiagnosticLog, P2SDiagnosticLog, RouteDiagnosticLog, TunnelDiagnosticLog]` | `[GatewayDiagnosticLog, IKEDiagnosticLog, P2SDiagnosticLog, RouteDiagnosticLog, TunnelDiagnosticLog]` | The name of logs that will be streamed. | +| `virtualNetworkGatewayDiagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `vpnClientAddressPoolPrefix` | string | `''` | | The IP address range from which VPN clients will receive an IP address when connected. Range specified must not overlap with on-premise network. | +| `vpnType` | string | `'RouteBased'` | `[PolicyBased, RouteBased]` | Specifies the VPN type. | + + +### Parameter Usage: `subnets` + +The `subnets` parameter accepts a JSON Array of `subnet` objects to deploy to the Virtual Network. + +Here's an example of specifying a couple Subnets to deploy: + +

+ +Parameter JSON format + +```json +"subnets": { + "value": [ + { + "name": "app", + "properties": { + "addressPrefix": "10.1.0.0/24", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', 'app-nsg')]" + }, + "routeTable": { + "id": "[resourceId('Microsoft.Network/routeTables', 'app-udr')]" + } + } + }, + { + "name": "data", + "properties": { + "addressPrefix": "10.1.1.0/24" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +subnets: [ + { + name: 'app' + properties: { + addressPrefix: '10.1.0.0/24' + networkSecurityGroup: { + id: '[resourceId('Microsoft.Network/networkSecurityGroups' 'app-nsg')]' + } + routeTable: { + id: '[resourceId('Microsoft.Network/routeTables' 'app-udr')]' + } + } + } + { + name: 'data' + properties: { + addressPrefix: '10.1.1.0/24' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `activeActive` | bool | Shows if the virtual network gateway is configured in active-active mode. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual network gateway. | +| `resourceGroupName` | string | The resource group the virtual network gateway was deployed. | +| `resourceId` | string | The resource ID of the virtual network gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Expressroute

+ +
+ +via Bicep module + +```bicep +module virtualNetworkGateways './Microsoft.Network/virtualNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualNetworkGateways' + params: { + // Required parameters + name: '<>-az-gw-er-001' + virtualNetworkGatewaySku: 'ErGw1AZ' + virtualNetworkGatewayType: 'ExpressRoute' + vNetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + domainNameLabel: [ + '<>-az-gw-er-dm-001' + ] + gatewayPipName: '<>-az-gw-er-001-pip' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Contact: 'test.user@testcompany.com' + CostCenter: '' + Environment: 'Validation' + PurchaseOrder: '' + Role: 'DeploymentValidation' + ServiceName: '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 + "name": { + "value": "<>-az-gw-er-001" + }, + "virtualNetworkGatewaySku": { + "value": "ErGw1AZ" + }, + "virtualNetworkGatewayType": { + "value": "ExpressRoute" + }, + "vNetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "domainNameLabel": { + "value": [ + "<>-az-gw-er-dm-001" + ] + }, + "gatewayPipName": { + "value": "<>-az-gw-er-001-pip" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Contact": "test.user@testcompany.com", + "CostCenter": "", + "Environment": "Validation", + "PurchaseOrder": "", + "Role": "DeploymentValidation", + "ServiceName": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Vpn

+ +
+ +via Bicep module + +```bicep +module virtualNetworkGateways './Microsoft.Network/virtualNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualNetworkGateways' + params: { + // Required parameters + name: '<>-az-gw-vpn-001' + virtualNetworkGatewaySku: 'VpnGw1AZ' + virtualNetworkGatewayType: 'Vpn' + vNetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001' + // Non-required parameters + activeActive: true + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + domainNameLabel: [ + '<>-az-gw-vpn-dm-001' + ] + lock: 'CanNotDelete' + publicIpZones: [ + '1' + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + vpnType: 'RouteBased' + } +} +``` + +
+

+ +

+ +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": "<>-az-gw-vpn-001" + }, + "virtualNetworkGatewaySku": { + "value": "VpnGw1AZ" + }, + "virtualNetworkGatewayType": { + "value": "Vpn" + }, + "vNetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001" + }, + // Non-required parameters + "activeActive": { + "value": true + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "domainNameLabel": { + "value": [ + "<>-az-gw-vpn-dm-001" + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "publicIpZones": { + "value": [ + "1" + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/virtualNetworkGateways/version.json b/modules/Microsoft.Network/virtualNetworkGateways/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworkGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..b0b9188 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,85 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualNetwork.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualNetwork +}] diff --git a/modules/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep b/modules/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep new file mode 100644 index 0000000..a2fb172 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep @@ -0,0 +1,35 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource routeTable 'Microsoft.Network/routeTables@2022-01-01' = { + name: routeTableName + location: location +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-01-01' = { + name: networkSecurityGroupName + location: location +} + +@description('The resource ID of the created Route Table.') +output routeTableResourceId string = routeTable.id + +@description('The resource ID of the created Network Security Group.') +output networkSecurityGroupResourceId string = networkSecurityGroup.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/modules/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep b/modules/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep new file mode 100644 index 0000000..43b2153 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep @@ -0,0 +1,128 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvncom' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + routeTableName: 'dep-<>-rt-${serviceShort}' + networkSecurityGroupName: 'dep-<>-nsg-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/dependencyConstructs/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.0.0.0/16' + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + dnsServers: [ + '10.0.1.4' + '10.0.1.5' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + subnets: [ + { + addressPrefix: '10.0.255.0/24' + name: 'GatewaySubnet' + } + { + addressPrefix: '10.0.0.0/24' + name: '<>-az-subnet-x-001' + networkSecurityGroupId: resourceGroupResources.outputs.networkSecurityGroupResourceId + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + routeTableId: resourceGroupResources.outputs.routeTableResourceId + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + } + { + addressPrefix: '10.0.3.0/24' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + name: '<>-az-subnet-x-002' + } + { + addressPrefix: '10.0.6.0/24' + name: '<>-az-subnet-x-003' + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + ] + } +} diff --git a/modules/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep b/modules/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep new file mode 100644 index 0000000..bb1b226 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep @@ -0,0 +1,40 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvnmin' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.0.0.0/16' + ] + } +} diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep b/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep new file mode 100644 index 0000000..60f4350 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep @@ -0,0 +1,28 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.1.0.0/24' + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: '10.1.0.0/24' + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep b/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep new file mode 100644 index 0000000..283ff8b --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvnpeer' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.0.0.0/24' + ] + subnets: [ + { + addressPrefix: '10.0.0.0/26' + name: 'GatewaySubnet' + } + ] + virtualNetworkPeerings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringEnabled: true + remotePeeringName: 'customName' + remoteVirtualNetworkId: resourceGroupResources.outputs.virtualNetworkResourceId + useRemoteGateways: false + } + ] + } +} diff --git a/modules/Microsoft.Network/virtualNetworks/deploy.bicep b/modules/Microsoft.Network/virtualNetworks/deploy.bicep new file mode 100644 index 0000000..0b84d70 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/deploy.bicep @@ -0,0 +1,267 @@ +@description('Required. The Virtual Network (vNet) Name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. An Array of 1 or more IP Address Prefixes for the Virtual Network.') +param addressPrefixes array + +@description('Optional. An Array of subnets to deploy to the Virtual Network.') +param subnets array = [] + +@description('Optional. DNS Servers associated to the Virtual Network.') +param dnsServers array = [] + +@description('Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it\'s left blank, DDoS protection will not be configured. If it\'s provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription.') +param ddosProtectionPlanId string = '' + +@description('Optional. Virtual Network Peerings configurations.') +param virtualNetworkPeerings array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'VMProtectionAlerts' +]) +param diagnosticLogCategoriesToEnable array = [ + 'VMProtectionAlerts' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var dnsServers_var = { + dnsServers: array(dnsServers) +} + +var ddosProtectionPlan = { + id: ddosProtectionPlanId +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + addressSpace: { + addressPrefixes: addressPrefixes + } + ddosProtectionPlan: !empty(ddosProtectionPlanId) ? ddosProtectionPlan : null + dhcpOptions: !empty(dnsServers) ? dnsServers_var : null + enableDdosProtection: !empty(ddosProtectionPlanId) + subnets: [for subnet in subnets: { + name: subnet.name + properties: { + addressPrefix: subnet.addressPrefix + addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] + applicationGatewayIpConfigurations: contains(subnet, 'applicationGatewayIpConfigurations') ? subnet.applicationGatewayIpConfigurations : [] + delegations: contains(subnet, 'delegations') ? subnet.delegations : [] + ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] + natGateway: contains(subnet, 'natGatewayId') ? { + 'id': subnet.natGatewayId + } : null + networkSecurityGroup: contains(subnet, 'networkSecurityGroupId') ? { + 'id': subnet.networkSecurityGroupId + } : null + privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') ? subnet.privateEndpointNetworkPolicies : null + privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') ? subnet.privateLinkServiceNetworkPolicies : null + routeTable: contains(subnet, 'routeTableId') ? { + 'id': subnet.routeTableId + } : null + serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] + serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] + } + }] + } +} + +//NOTE Start: ------------------------------------ +// The below module (virtualNetwork_subnets) is a duplicate of the child resource (subnets) defined in the parent module (virtualNetwork). +// The reason it exists so that deployment validation tests can be performed on the child module (subnets), in case that module needed to be deployed alone outside of this template. +// The reason for duplication is due to the current design for the (virtualNetworks) resource from Azure, where if the child module (subnets) does not exist within it, causes +// an issue, where the child resource (subnets) gets all of its properties removed, hence not as 'idempotent' as it should be. See https://github.com/Azure/azure-quickstart-templates/issues/2786 for more details. +// You can safely remove the below child module (virtualNetwork_subnets) in your consumption of the module (virtualNetworks) to reduce the template size and duplication. +//NOTE End : ------------------------------------ + +module virtualNetwork_subnets 'subnets/deploy.bicep' = [for (subnet, index) in subnets: { + name: '${uniqueString(deployment().name, location)}-subnet-${index}' + params: { + virtualNetworkName: virtualNetwork.name + name: subnet.name + addressPrefix: subnet.addressPrefix + addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] + applicationGatewayIpConfigurations: contains(subnet, 'applicationGatewayIpConfigurations') ? subnet.applicationGatewayIpConfigurations : [] + delegations: contains(subnet, 'delegations') ? subnet.delegations : [] + ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] + natGatewayId: contains(subnet, 'natGatewayId') ? subnet.natGatewayId : '' + networkSecurityGroupId: contains(subnet, 'networkSecurityGroupId') ? subnet.networkSecurityGroupId : '' + privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') ? subnet.privateEndpointNetworkPolicies : '' + privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') ? subnet.privateLinkServiceNetworkPolicies : '' + roleAssignments: contains(subnet, 'roleAssignments') ? subnet.roleAssignments : [] + routeTableId: contains(subnet, 'routeTableId') ? subnet.routeTableId : '' + serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] + serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +// Local to Remote peering +module virtualNetwork_peering_local 'virtualNetworkPeerings/deploy.bicep' = [for (peering, index) in virtualNetworkPeerings: { + name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-local-${index}' + params: { + localVnetName: virtualNetwork.name + remoteVirtualNetworkId: peering.remoteVirtualNetworkId + name: contains(peering, 'name') ? peering.name : '${name}-${last(split(peering.remoteVirtualNetworkId, '/'))}' + allowForwardedTraffic: contains(peering, 'allowForwardedTraffic') ? peering.allowForwardedTraffic : true + allowGatewayTransit: contains(peering, 'allowGatewayTransit') ? peering.allowGatewayTransit : false + allowVirtualNetworkAccess: contains(peering, 'allowVirtualNetworkAccess') ? peering.allowVirtualNetworkAccess : true + doNotVerifyRemoteGateways: contains(peering, 'doNotVerifyRemoteGateways') ? peering.doNotVerifyRemoteGateways : true + useRemoteGateways: contains(peering, 'useRemoteGateways') ? peering.useRemoteGateways : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +// Remote to local peering (reverse) +module virtualNetwork_peering_remote 'virtualNetworkPeerings/deploy.bicep' = [for (peering, index) in virtualNetworkPeerings: if (contains(peering, 'remotePeeringEnabled') ? peering.remotePeeringEnabled == true : false) { + name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-remote-${index}' + scope: resourceGroup(split(peering.remoteVirtualNetworkId, '/')[2], split(peering.remoteVirtualNetworkId, '/')[4]) + params: { + localVnetName: last(split(peering.remoteVirtualNetworkId, '/')) + remoteVirtualNetworkId: virtualNetwork.id + name: contains(peering, 'remotePeeringName') ? peering.remotePeeringName : '${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}' + allowForwardedTraffic: contains(peering, 'remotePeeringAllowForwardedTraffic') ? peering.remotePeeringAllowForwardedTraffic : true + allowGatewayTransit: contains(peering, 'remotePeeringAllowGatewayTransit') ? peering.remotePeeringAllowGatewayTransit : false + allowVirtualNetworkAccess: contains(peering, 'remotePeeringAllowVirtualNetworkAccess') ? peering.remotePeeringAllowVirtualNetworkAccess : true + doNotVerifyRemoteGateways: contains(peering, 'remotePeeringDoNotVerifyRemoteGateways') ? peering.remotePeeringDoNotVerifyRemoteGateways : true + useRemoteGateways: contains(peering, 'remotePeeringUseRemoteGateways') ? peering.remotePeeringUseRemoteGateways : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource virtualNetwork_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${virtualNetwork.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualNetwork +} + +resource virtualNetwork_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: virtualNetwork +} + +module virtualNetwork_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VNet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualNetwork.id + } +}] + +@description('The resource group the virtual network was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual network.') +output resourceId string = virtualNetwork.id + +@description('The name of the virtual network.') +output name string = virtualNetwork.name + +@description('The names of the deployed subnets.') +output subnetNames array = [for subnet in subnets: subnet.name] + +@description('The resource IDs of the deployed subnets.') +output subnetResourceIds array = [for subnet in subnets: az.resourceId('Microsoft.Network/virtualNetworks/subnets', name, subnet.name)] + +@description('The location the resource was deployed into.') +output location string = virtualNetwork.location diff --git a/modules/Microsoft.Network/virtualNetworks/readme.md b/modules/Microsoft.Network/virtualNetworks/readme.md new file mode 100644 index 0000000..325be20 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/readme.md @@ -0,0 +1,692 @@ +# Virtual Networks `[Microsoft.Network/virtualNetworks]` + +This template deploys a virtual network (vNet). + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/virtualNetworks` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworks/virtualNetworkPeerings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefixes` | array | An Array of 1 or more IP Address Prefixes for the Virtual Network. | +| `name` | string | The Virtual Network (vNet) Name. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `ddosProtectionPlanId` | string | `''` | | Resource ID of the DDoS protection plan to assign the VNET to. If it's left blank, DDoS protection will not be configured. If it's provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[VMProtectionAlerts]` | `[VMProtectionAlerts]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `dnsServers` | array | `[]` | | DNS Servers associated to the Virtual Network. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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'. | +| `subnets` | _[subnets](subnets/readme.md)_ array | `[]` | | An Array of subnets to deploy to the Virtual Network. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `virtualNetworkPeerings` | _[virtualNetworkPeerings](virtualNetworkPeerings/readme.md)_ array | `[]` | | Virtual Network Peerings configurations. | + + +### Parameter Usage: `subnets` + +Below you can find an example for the subnet property's usage. For all remaining properties, please refer to the _[subnets](subnets/readme.md)_ readme. + +

+ +Template JSON format + +```json +"subnets": { + "value": [ + { + "name": "GatewaySubnet", + "addressPrefix": "10.0.255.0/24" + }, + { + "name": "<>-az-subnet-x-001", + "addressPrefix": "10.0.0.0/24", + "networkSecurityGroupId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + }, + { + "service": "Microsoft.Sql" + } + ], + "routeTableId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/routeTables/adp-<>-az-udr-x-001", + "delegations": [ + { + "name": "netappDel", + "properties": { + "serviceName": "Microsoft.Netapp/volumes" + } + } + ], + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: '10.0.255.0/24' + } + { + name: '<>-az-subnet-x-001' + addressPrefix: '10.0.0.0/24' + networkSecurityGroupId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001' + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + routeTableId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/routeTables/adp-<>-az-udr-x-001' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } +] +``` + +
+

+ +### Parameter Usage: `virtualNetworkPeerings` + +As the virtual network peering array allows you to deploy not only a one-way but also two-way peering (i.e reverse), you can use the following ***additional*** properties on top of what is documented in _[virtualNetworkPeerings](virtualNetworkPeerings/readme.md)_. + +| Parameter Name | Type | Default Value | Possible Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `remotePeeringEnabled` | bool | `false` | | Optional. Set to true to also deploy the reverse peering for the configured remote virtual networks to the local network | +| `remotePeeringName` | string | `'${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}'` | | Optional. The Name of Vnet Peering resource. If not provided, default value will be - | +| `remotePeeringAllowForwardedTraffic` | bool | `true` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. | +| `remotePeeringAllowGatewayTransit` | bool | `false` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network. | +| `remotePeeringAllowVirtualNetworkAccess` | bool | `true` | | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. | +| `remotePeeringDoNotVerifyRemoteGateways` | bool | `true` | | Optional. If we need to verify the provisioning state of the remote gateway. | +| `remotePeeringUseRemoteGateways` | bool | `false` | | Optional. If remote gateways can be used on this virtual network. If the flag is set to `true`, and allowGatewayTransit on local peering is also `true`, virtual network will use gateways of local virtual network for transit. Only one peering can have this flag set to `true`. This flag cannot be set if virtual network already has a gateway. | + +

+ +Parameter JSON format + +```json +"virtualNetworkPeerings": { + "value": [ + { + "remoteVirtualNetworkId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-peer01", + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "useRemoteGateways": false, + "remotePeeringEnabled": true, + "remotePeeringName": "customName", + "remotePeeringAllowVirtualNetworkAccess": true, + "remotePeeringAllowForwardedTraffic": true + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkPeerings: [ + { + remoteVirtualNetworkId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-peer01' + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + useRemoteGateways: false + remotePeeringEnabled: true + remotePeeringName: 'customName' + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + } +] +``` + +
+

+ +### Parameter Usage: `addressPrefixes` + +The `addressPrefixes` parameter accepts a JSON Array of string values containing the IP Address Prefixes for the Virtual Network (vNet). + +Here's an example of specifying a single Address Prefix: + + +

+ +Parameter JSON format + +```json +"addressPrefixes": { + "value": [ + "10.1.0.0/16" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +addressPrefixes: [ + '10.1.0.0/16' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Considerations + +The network security group and route table resources must reside in the same resource group as the virtual network. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual network. | +| `resourceGroupName` | string | The resource group the virtual network was deployed into. | +| `resourceId` | string | The resource ID of the virtual network. | +| `subnetNames` | array | The names of the deployed subnets. | +| `subnetResourceIds` | array | The resource IDs of the deployed subnets. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-nvncom' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + name: '<>nvncom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + dnsServers: [ + '10.0.1.4' + '10.0.1.5' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + subnets: [ + { + addressPrefix: '10.0.255.0/24' + name: 'GatewaySubnet' + } + { + addressPrefix: '10.0.0.0/24' + name: '<>-az-subnet-x-001' + networkSecurityGroupId: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + routeTableId: '' + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + } + { + addressPrefix: '10.0.3.0/24' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + name: '<>-az-subnet-x-002' + } + { + addressPrefix: '10.0.6.0/24' + name: '<>-az-subnet-x-003' + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + ] + } +} +``` + +
+

+ +

+ +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 + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "name": { + "value": "<>nvncom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "dnsServers": { + "value": [ + "10.0.1.4", + "10.0.1.5" + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "subnets": { + "value": [ + { + "addressPrefix": "10.0.255.0/24", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "10.0.0.0/24", + "name": "<>-az-subnet-x-001", + "networkSecurityGroupId": "", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "routeTableId": "", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + }, + { + "service": "Microsoft.Sql" + } + ] + }, + { + "addressPrefix": "10.0.3.0/24", + "delegations": [ + { + "name": "netappDel", + "properties": { + "serviceName": "Microsoft.Netapp/volumes" + } + } + ], + "name": "<>-az-subnet-x-002" + }, + { + "addressPrefix": "10.0.6.0/24", + "name": "<>-az-subnet-x-003", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-nvnmin' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + name: '<>nvnmin001' + } +} +``` + +
+

+ +

+ +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 + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "name": { + "value": "<>nvnmin001" + } + } +} +``` + +
+

+ +

Example 3: Vnetpeering

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-nvnpeer' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/24' + ] + name: '<>nvnpeer001' + // Non-required parameters + subnets: [ + { + addressPrefix: '10.0.0.0/26' + name: 'GatewaySubnet' + } + ] + virtualNetworkPeerings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringEnabled: true + remotePeeringName: 'customName' + remoteVirtualNetworkId: '' + useRemoteGateways: false + } + ] + } +} +``` + +
+

+ +

+ +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 + "addressPrefixes": { + "value": [ + "10.0.0.0/24" + ] + }, + "name": { + "value": "<>nvnpeer001" + }, + // Non-required parameters + "subnets": { + "value": [ + { + "addressPrefix": "10.0.0.0/26", + "name": "GatewaySubnet" + } + ] + }, + "virtualNetworkPeerings": { + "value": [ + { + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "remotePeeringAllowForwardedTraffic": true, + "remotePeeringAllowVirtualNetworkAccess": true, + "remotePeeringEnabled": true, + "remotePeeringName": "customName", + "remoteVirtualNetworkId": "", + "useRemoteGateways": false + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..c70a83d --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,85 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-03-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(subnet.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: subnet +}] diff --git a/modules/Microsoft.Network/virtualNetworks/subnets/deploy.bicep b/modules/Microsoft.Network/virtualNetworks/subnets/deploy.bicep new file mode 100644 index 0000000..898fc07 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/subnets/deploy.bicep @@ -0,0 +1,126 @@ +@description('Optional. The Name of the subnet resource.') +param name string + +@description('Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment.') +param virtualNetworkName string + +@description('Required. The address prefix for the subnet.') +param addressPrefix string + +@description('Optional. The resource ID of the network security group to assign to the subnet.') +param networkSecurityGroupId string = '' + +@description('Optional. The resource ID of the route table to assign to the subnet.') +param routeTableId string = '' + +@description('Optional. The service endpoints to enable on the subnet.') +param serviceEndpoints array = [] + +@description('Optional. The delegations to enable on the subnet.') +param delegations array = [] + +@description('Optional. The resource ID of the NAT Gateway to use for the subnet.') +param natGatewayId string = '' + +@description('Optional. enable or disable apply network policies on private endpoint in the subnet.') +@allowed([ + 'Disabled' + 'Enabled' + '' +]) +param privateEndpointNetworkPolicies string = '' + +@description('Optional. enable or disable apply network policies on private link service in the subnet.') +@allowed([ + 'Disabled' + 'Enabled' + '' +]) +param privateLinkServiceNetworkPolicies string = '' + +@description('Optional. List of address prefixes for the subnet.') +param addressPrefixes array = [] + +@description('Optional. Application gateway IP configurations of virtual network resource.') +param applicationGatewayIpConfigurations array = [] + +@description('Optional. Array of IpAllocation which reference this subnet.') +param ipAllocations array = [] + +@description('Optional. An array of service endpoint policies.') +param serviceEndpointPolicies array = [] + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { + name: virtualNetworkName +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' = { + name: name + parent: virtualNetwork + properties: { + addressPrefix: addressPrefix + networkSecurityGroup: !empty(networkSecurityGroupId) ? { + id: networkSecurityGroupId + } : null + routeTable: !empty(routeTableId) ? { + id: routeTableId + } : null + natGateway: !empty(natGatewayId) ? { + id: natGatewayId + } : null + serviceEndpoints: serviceEndpoints + delegations: delegations + privateEndpointNetworkPolicies: !empty(privateEndpointNetworkPolicies) ? any(privateEndpointNetworkPolicies) : null + privateLinkServiceNetworkPolicies: !empty(privateLinkServiceNetworkPolicies) ? any(privateLinkServiceNetworkPolicies) : null + addressPrefixes: addressPrefixes + applicationGatewayIpConfigurations: applicationGatewayIpConfigurations + ipAllocations: ipAllocations + serviceEndpointPolicies: serviceEndpointPolicies + } +} + +module subnet_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, subnet.id)}-Subnet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: subnet.id + } +}] + +@description('The resource group the virtual network peering was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network peering.') +output name string = subnet.name + +@description('The resource ID of the virtual network peering.') +output resourceId string = subnet.id + +@description('The address prefix for the subnet.') +output subnetAddressPrefix string = subnet.properties.addressPrefix + +@description('List of address prefixes for the subnet.') +output subnetAddressPrefixes array = !empty(addressPrefixes) ? subnet.properties.addressPrefixes : [] diff --git a/modules/Microsoft.Network/virtualNetworks/subnets/readme.md b/modules/Microsoft.Network/virtualNetworks/subnets/readme.md new file mode 100644 index 0000000..60619d5 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/subnets/readme.md @@ -0,0 +1,197 @@ +# Virtual Network Subnets `[Microsoft.Network/virtualNetworks/subnets]` + +This module deploys a virtual network subnet. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/virtualNetworks/subnets` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworks/subnets) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefix` | string | The address prefix for the subnet. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualNetworkName` | string | The name of the parent virtual network. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `addressPrefixes` | array | `[]` | | List of address prefixes for the subnet. | +| `applicationGatewayIpConfigurations` | array | `[]` | | Application gateway IP configurations of virtual network resource. | +| `delegations` | array | `[]` | | The delegations to enable on the subnet. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ipAllocations` | array | `[]` | | Array of IpAllocation which reference this subnet. | +| `name` | string | | | The Name of the subnet resource. | +| `natGatewayId` | string | `''` | | The resource ID of the NAT Gateway to use for the subnet. | +| `networkSecurityGroupId` | string | `''` | | The resource ID of the network security group to assign to the subnet. | +| `privateEndpointNetworkPolicies` | string | `''` | `['', Disabled, Enabled]` | enable or disable apply network policies on private endpoint in the subnet. | +| `privateLinkServiceNetworkPolicies` | string | `''` | `['', Disabled, Enabled]` | enable or disable apply network policies on private link service in the subnet. | +| `roleAssignments` | array | `[]` | | 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'. | +| `routeTableId` | string | `''` | | The resource ID of the route table to assign to the subnet. | +| `serviceEndpointPolicies` | array | `[]` | | An array of service endpoint policies. | +| `serviceEndpoints` | array | `[]` | | The service endpoints to enable on the subnet. | + + +### Parameter Usage: `delegations` + +

+ +Parameter JSON format + +```json +"delegations": [ + { + "name": "sqlMiDel", + "properties": { + "serviceName": "Microsoft.Sql/managedInstances" + } + } +] +``` + +
+ +
+ +Bicep format + +```bicep +delegations: [ + { + name: 'sqlMiDel' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } +] +``` + +
+

+ +### Parameter Usage: `serviceEndpoints` + +

+ +Parameter JSON format + +```json +"serviceEndpoints": [ + "Microsoft.EventHub", + "Microsoft.Sql", + "Microsoft.Storage", + "Microsoft.KeyVault" +] +``` + +
+ + +
+ +Bicep format + +```bicep +serviceEndpoints: [ + 'Microsoft.EventHub' + 'Microsoft.Sql' + 'Microsoft.Storage' + 'Microsoft.KeyVault' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Considerations + +The `privateEndpointNetworkPolicies` property must be set to disabled for subnets that contain private endpoints. It confirms that NSGs rules will not apply to private endpoints (currently not supported, [reference](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-overview#limitations)). Default Value when not specified is "Enabled". + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual network peering. | +| `resourceGroupName` | string | The resource group the virtual network peering was deployed into. | +| `resourceId` | string | The resource ID of the virtual network peering. | +| `subnetAddressPrefix` | string | The address prefix for the subnet. | +| `subnetAddressPrefixes` | array | List of address prefixes for the subnet. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/virtualNetworks/subnets/version.json b/modules/Microsoft.Network/virtualNetworks/subnets/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/subnets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualNetworks/version.json b/modules/Microsoft.Network/virtualNetworks/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep new file mode 100644 index 0000000..02261f2 --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep @@ -0,0 +1,66 @@ +@description('Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName.') +param name string = '${localVnetName}-${last(split(remoteVirtualNetworkId, '/'))}' + +@description('Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment.') +param localVnetName string + +@description('Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID.') +param remoteVirtualNetworkId string + +@description('Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true.') +param allowForwardedTraffic bool = true + +@description('Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false.') +param allowGatewayTransit bool = false + +@description('Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true.') +param allowVirtualNetworkAccess bool = true + +@description('Optional. If we need to verify the provisioning state of the remote gateway. Default is true.') +param doNotVerifyRemoteGateways bool = true + +@description('Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false.') +param useRemoteGateways bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { + name: localVnetName +} + +resource virtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-08-01' = { + name: name + parent: virtualNetwork + properties: { + allowForwardedTraffic: allowForwardedTraffic + allowGatewayTransit: allowGatewayTransit + allowVirtualNetworkAccess: allowVirtualNetworkAccess + doNotVerifyRemoteGateways: doNotVerifyRemoteGateways + useRemoteGateways: useRemoteGateways + remoteVirtualNetwork: { + id: remoteVirtualNetworkId + } + } +} + +@description('The resource group the virtual network peering was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network peering.') +output name string = virtualNetworkPeering.name + +@description('The resource ID of the virtual network peering.') +output resourceId string = virtualNetworkPeering.id diff --git a/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md new file mode 100644 index 0000000..55a11de --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md @@ -0,0 +1,59 @@ +# VirtualNetworkPeering `[Microsoft.Network/virtualNetworks/virtualNetworkPeerings]` + +This template deploys Virtual Network Peering. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualNetworks/virtualNetworkPeerings) | + +### Resource dependency + +The following resources are required to be able to deploy this resource. + +- Local Virtual Network (Identified by the `localVnetName` parameter). +- Remote Virtual Network (Identified by the `remoteVirtualNetworkId` parameter) + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `remoteVirtualNetworkId` | string | The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `localVnetName` | string | The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `allowForwardedTraffic` | bool | `True` | Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. | +| `allowGatewayTransit` | bool | `False` | If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. | +| `allowVirtualNetworkAccess` | bool | `True` | Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. | +| `doNotVerifyRemoteGateways` | bool | `True` | If we need to verify the provisioning state of the remote gateway. Default is true. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]` | The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName. | +| `useRemoteGateways` | bool | `False` | If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual network peering. | +| `resourceGroupName` | string | The resource group the virtual network peering was deployed into. | +| `resourceId` | string | The resource ID of the virtual network peering. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..49253ba --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 virtualWan 'Microsoft.Network/virtualWans@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualWan.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualWan +}] diff --git a/modules/Microsoft.Network/virtualWans/.test/min.parameters.json b/modules/Microsoft.Network/virtualWans/.test/min.parameters.json new file mode 100644 index 0000000..badddff --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/.test/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": "<>-az-vw-min-001" + } + } +} diff --git a/modules/Microsoft.Network/virtualWans/.test/parameters.json b/modules/Microsoft.Network/virtualWans/.test/parameters.json new file mode 100644 index 0000000..15f8aa9 --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/.test/parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vw-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "type": { + "value": "Basic" + }, + "allowBranchToBranchTraffic": { + "value": true + }, + "allowVnetToVnetTraffic": { + "value": true + }, + "disableVpnEncryption": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/virtualWans/deploy.bicep b/modules/Microsoft.Network/virtualWans/deploy.bicep new file mode 100644 index 0000000..ffd0770 --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/deploy.bicep @@ -0,0 +1,96 @@ +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Required. Name of the Virtual WAN.') +param name string + +@description('Optional. The type of the Virtual WAN.') +@allowed([ + 'Standard' + 'Basic' +]) +param type string = 'Standard' + +@description('Optional. True if branch to branch traffic is allowed.') +param allowBranchToBranchTraffic bool = false + +@description('Optional. True if VNET to VNET traffic is allowed.') +param allowVnetToVnetTraffic bool = false + +@description('Optional. VPN encryption to be disabled or not.') +param disableVpnEncryption bool = false + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualWan 'Microsoft.Network/virtualWans@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + allowBranchToBranchTraffic: allowBranchToBranchTraffic + allowVnetToVnetTraffic: allowVnetToVnetTraffic ? allowVnetToVnetTraffic : null + disableVpnEncryption: disableVpnEncryption + type: type + } +} + +resource virtualWan_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${virtualWan.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualWan +} + +module virtualWan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VWan-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualWan.id + } +}] + +@description('The name of the virtual WAN.') +output name string = virtualWan.name + +@description('The resource ID of the virtual WAN.') +output resourceId string = virtualWan.id + +@description('The resource group the virtual WAN was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = virtualWan.location diff --git a/modules/Microsoft.Network/virtualWans/readme.md b/modules/Microsoft.Network/virtualWans/readme.md new file mode 100644 index 0000000..bc125b6 --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/readme.md @@ -0,0 +1,276 @@ +# Virtual WANs `[Microsoft.Network/virtualWans]` + +This template deploys a virtual WAN. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/virtualWans` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/virtualWans) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Virtual WAN. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowBranchToBranchTraffic` | bool | `False` | | True if branch to branch traffic is allowed. | +| `allowVnetToVnetTraffic` | bool | `False` | | True if VNET to VNET traffic is allowed. | +| `disableVpnEncryption` | bool | `False` | | VPN encryption to be disabled or not. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | +| `type` | string | `'Standard'` | `[Basic, Standard]` | The type of the Virtual WAN. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual WAN. | +| `resourceGroupName` | string | The resource group the virtual WAN was deployed into. | +| `resourceId` | string | The resource ID of the virtual WAN. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module virtualWans './Microsoft.Network/virtualWans/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualWans' + params: { + name: '<>-az-vw-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vw-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module virtualWans './Microsoft.Network/virtualWans/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VirtualWans' + params: { + // Required parameters + name: '<>-az-vw-x-001' + // Non-required parameters + allowBranchToBranchTraffic: true + allowVnetToVnetTraffic: true + disableVpnEncryption: true + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + type: 'Basic' + } +} +``` + +
+

+ +

+ +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": "<>-az-vw-x-001" + }, + // Non-required parameters + "allowBranchToBranchTraffic": { + "value": true + }, + "allowVnetToVnetTraffic": { + "value": true + }, + "disableVpnEncryption": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "type": { + "value": "Basic" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/virtualWans/version.json b/modules/Microsoft.Network/virtualWans/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Network/virtualWans/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Network/vpnGateways/.test/min.parameters.json b/modules/Microsoft.Network/vpnGateways/.test/min.parameters.json new file mode 100644 index 0000000..4ed3a73 --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vpngw-min-001" + }, + "virtualHubResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-min-001" + } + } +} diff --git a/modules/Microsoft.Network/vpnGateways/.test/parameters.json b/modules/Microsoft.Network/vpnGateways/.test/parameters.json new file mode 100644 index 0000000..620e1c6 --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/.test/parameters.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vpngw-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "virtualHubResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001" + }, + "bgpSettings": { + "value": { + "asn": 65515, + "peerWeight": 0 + } + }, + "connections": { + "value": [ + { + "name": "Connection-<>-az-vsite-x-001", + "connectionBandwidth": 10, + "enableBgp": true, + "routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable" + }, + "propagatedRouteTables": { + "labels": [ + "default" + ], + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable" + } + ] + }, + "vnetRoutes": { + "staticRoutes": [] + } + }, + "remoteVpnSiteResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/vpnSites/<>-az-vsite-x-001" + } + ] + }, + "natRules": { + "value": [ + { + "name": "natRule1", + "internalMappings": [ + { + "addressSpace": "10.4.0.0/24" + } + ], + "externalMappings": [ + { + "addressSpace": "192.168.21.0/24" + } + ], + "type": "Static", + "mode": "EgressSnat" + } + ] + } + } +} diff --git a/modules/Microsoft.Network/vpnGateways/connections/deploy.bicep b/modules/Microsoft.Network/vpnGateways/connections/deploy.bicep new file mode 100644 index 0000000..f5309ca --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/connections/deploy.bicep @@ -0,0 +1,102 @@ +@description('Required. The name of the VPN connection.') +param name string + +@description('Conditional. The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment.') +param vpnGatewayName string + +@description('Optional. The IPSec policies to be considered by this connection.') +param ipsecPolicies array = [] + +@description('Optional. The traffic selector policies to be considered by this connection.') +param trafficSelectorPolicies array = [] + +@description('Optional. List of all VPN site link connections to the gateway.') +param vpnLinkConnections array = [] + +@description('Optional. Routing configuration indicating the associated and propagated route tables for this connection.') +param routingConfiguration object = {} + +@description('Optional. Enable policy-based traffic selectors.') +param usePolicyBasedTrafficSelectors bool = false + +@description('Optional. Use local Azure IP to initiate connection.') +param useLocalAzureIpAddress bool = false + +@description('Optional. Enable rate limiting.') +param enableRateLimiting bool = false + +@description('Optional. Enable internet security.') +param enableInternetSecurity bool = false + +@description('Optional. Enable BGP flag.') +param enableBgp bool = false + +@description('Optional. Routing weight for VPN connection.') +param routingWeight int = 0 + +@description('Optional. Expected bandwidth in MBPS.') +param connectionBandwidth int = 10 + +@description('Optional. Gateway connection protocol.') +@allowed([ + 'IKEv1' + 'IKEv2' +]) +param vpnConnectionProtocolType string = 'IKEv2' + +@description('Optional. SharedKey for the VPN connection.') +param sharedKey string = '' + +@description('Optional. Reference to a VPN site to link to.') +param remoteVpnSiteResourceId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2021-08-01' existing = { + name: vpnGatewayName +} + +resource vpnConnection 'Microsoft.Network/vpnGateways/vpnConnections@2021-08-01' = { + name: name + parent: vpnGateway + properties: { + connectionBandwidth: connectionBandwidth + enableBgp: enableBgp + enableInternetSecurity: enableInternetSecurity + enableRateLimiting: enableRateLimiting + ipsecPolicies: ipsecPolicies + remoteVpnSite: !empty(remoteVpnSiteResourceId) ? { + id: remoteVpnSiteResourceId + } : null + routingConfiguration: routingConfiguration + routingWeight: routingWeight + sharedKey: sharedKey + trafficSelectorPolicies: trafficSelectorPolicies + useLocalAzureIpAddress: useLocalAzureIpAddress + usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors + vpnConnectionProtocolType: vpnConnectionProtocolType + vpnLinkConnections: vpnLinkConnections + } +} + +@description('The name of the VPN connection.') +output name string = vpnConnection.name + +@description('The resource ID of the VPN connection.') +output resourceId string = vpnConnection.id + +@description('The name of the resource group the VPN connection was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/vpnGateways/connections/readme.md b/modules/Microsoft.Network/vpnGateways/connections/readme.md new file mode 100644 index 0000000..5e8e9ad --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/connections/readme.md @@ -0,0 +1,117 @@ +# VPN Gateways Connections `[Microsoft.Network/vpnGateways/connections]` + +This module deploys VPN Gateways Connections. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/vpnGateways/vpnConnections` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnGateways/vpnConnections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the VPN connection. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `vpnGatewayName` | string | The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectionBandwidth` | int | `10` | | Expected bandwidth in MBPS. | +| `enableBgp` | bool | `False` | | Enable BGP flag. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableInternetSecurity` | bool | `False` | | Enable internet security. | +| `enableRateLimiting` | bool | `False` | | Enable rate limiting. | +| `ipsecPolicies` | array | `[]` | | The IPSec policies to be considered by this connection. | +| `remoteVpnSiteResourceId` | string | `''` | | Reference to a VPN site to link to. | +| `routingConfiguration` | object | `{object}` | | Routing configuration indicating the associated and propagated route tables for this connection. | +| `routingWeight` | int | `0` | | Routing weight for VPN connection. | +| `sharedKey` | string | `''` | | SharedKey for the VPN connection. | +| `trafficSelectorPolicies` | array | `[]` | | The traffic selector policies to be considered by this connection. | +| `useLocalAzureIpAddress` | bool | `False` | | Use local Azure IP to initiate connection. | +| `usePolicyBasedTrafficSelectors` | bool | `False` | | Enable policy-based traffic selectors. | +| `vpnConnectionProtocolType` | string | `'IKEv2'` | `[IKEv1, IKEv2]` | Gateway connection protocol. | +| `vpnLinkConnections` | array | `[]` | | List of all VPN site link connections to the gateway. | + + +### Parameter Usage: `routingConfiguration` + +

+ +Parameter JSON format + +```json +"routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable" + }, + "propagatedRouteTables": { + "labels": [ + "default" + ], + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable" + } + ] + }, + "vnetRoutes": { + "staticRoutes": [] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +routingConfiguration: { + associatedRouteTable: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable' + } + propagatedRouteTables: { + labels: [ + 'default' + ] + ids: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable' + } + ] + } + vnetRoutes: { + staticRoutes: [] + } +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the VPN connection. | +| `resourceGroupName` | string | The name of the resource group the VPN connection was deployed into. | +| `resourceId` | string | The resource ID of the VPN connection. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/vpnGateways/connections/version.json b/modules/Microsoft.Network/vpnGateways/connections/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/connections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/vpnGateways/deploy.bicep b/modules/Microsoft.Network/vpnGateways/deploy.bicep new file mode 100644 index 0000000..aaebbec --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/deploy.bicep @@ -0,0 +1,124 @@ +@description('Required. Name of the VPN gateway.') +param name string + +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Optional. The connections to create in the VPN gateway.') +param connections array = [] + +@description('Optional. List of all the NAT Rules to associate with the gateway.') +param natRules array = [] + +@description('Required. The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location.') +param virtualHubResourceId string + +@description('Optional. BGP settings details.') +param bgpSettings object = {} + +@description('Optional. Enable BGP routes translation for NAT on this VPN gateway.') +param enableBgpRouteTranslationForNat bool = false + +@description('Optional. Enable routing preference property for the public IP interface of the VPN gateway.') +param isRoutingPreferenceInternet bool = false + +@description('Optional. The scale unit for this VPN gateway.') +param vpnGatewayScaleUnit int = 2 + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + bgpSettings: bgpSettings + enableBgpRouteTranslationForNat: enableBgpRouteTranslationForNat + isRoutingPreferenceInternet: isRoutingPreferenceInternet + vpnGatewayScaleUnit: vpnGatewayScaleUnit + virtualHub: { + id: virtualHubResourceId + } + } +} + +resource vpnGateway_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${vpnGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vpnGateway +} + +module vpnGateway_natRules 'natRules/deploy.bicep' = [for (natRule, index) in natRules: { + name: '${deployment().name}-NATRule-${index}' + params: { + name: natRule.name + vpnGatewayName: vpnGateway.name + externalMappings: contains(natRule, 'externalMappings') ? natRule.externalMappings : [] + internalMappings: contains(natRule, 'internalMappings') ? natRule.internalMappings : [] + ipConfigurationId: contains(natRule, 'ipConfigurationId') ? natRule.ipConfigurationId : '' + mode: contains(natRule, 'mode') ? natRule.mode : '' + type: contains(natRule, 'type') ? natRule.type : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module vpnGateway_connections 'connections/deploy.bicep' = [for (connection, index) in connections: { + name: '${deployment().name}-Connection-${index}' + params: { + name: connection.name + vpnGatewayName: vpnGateway.name + connectionBandwidth: contains(connection, 'connectionBandwidth') ? connection.connectionBandwidth : 10 + enableBgp: contains(connection, 'enableBgp') ? connection.enableBgp : false + enableInternetSecurity: contains(connection, 'enableInternetSecurity') ? connection.enableInternetSecurity : false + remoteVpnSiteResourceId: contains(connection, 'remoteVpnSiteResourceId') ? connection.remoteVpnSiteResourceId : '' + enableRateLimiting: contains(connection, 'enableRateLimiting') ? connection.enableRateLimiting : false + routingConfiguration: contains(connection, 'routingConfiguration') ? connection.routingConfiguration : {} + routingWeight: contains(connection, 'routingWeight') ? connection.routingWeight : 0 + sharedKey: contains(connection, 'sharedKey') ? connection.sharedKey : '' + useLocalAzureIpAddress: contains(connection, 'useLocalAzureIpAddress') ? connection.useLocalAzureIpAddress : false + usePolicyBasedTrafficSelectors: contains(connection, 'usePolicyBasedTrafficSelectors') ? connection.usePolicyBasedTrafficSelectors : false + vpnConnectionProtocolType: contains(connection, 'vpnConnectionProtocolType') ? connection.vpnConnectionProtocolType : 'IKEv2' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the VPN gateway.') +output name string = vpnGateway.name + +@description('The resource ID of the VPN gateway.') +output resourceId string = vpnGateway.id + +@description('The name of the resource group the VPN gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = vpnGateway.location diff --git a/modules/Microsoft.Network/vpnGateways/natRules/deploy.bicep b/modules/Microsoft.Network/vpnGateways/natRules/deploy.bicep new file mode 100644 index 0000000..0ce8ca0 --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/natRules/deploy.bicep @@ -0,0 +1,70 @@ +@description('Required. The name of the NAT rule.') +param name string + +@description('Conditional. The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment.') +param vpnGatewayName string + +@description('Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range.') +param externalMappings array = [] + +@description('Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range.') +param internalMappings array = [] + +@description('Optional. A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances.') +param ipConfigurationId string = '' + +@description('Optional. The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub\'s site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub\'s Site-to-site VPN gateway.') +@allowed([ + '' + 'EgressSnat' + 'IngressSnat' +]) +param mode string = '' + +@description('Optional. The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability.') +@allowed([ + '' + 'Dynamic' + 'Static' +]) +param type string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2021-08-01' existing = { + name: vpnGatewayName +} + +resource natRule 'Microsoft.Network/vpnGateways/natRules@2021-08-01' = { + name: name + parent: vpnGateway + properties: { + externalMappings: externalMappings + internalMappings: internalMappings + ipConfigurationId: !empty(ipConfigurationId) ? ipConfigurationId : null + mode: !empty(mode) ? any(mode) : null + type: !empty(type) ? any(type) : null + } +} + +@description('The name of the NAT rule.') +output name string = natRule.name + +@description('The resource ID of the NAT rule.') +output resourceId string = natRule.id + +@description('The name of the resource group the NAT rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Network/vpnGateways/natRules/readme.md b/modules/Microsoft.Network/vpnGateways/natRules/readme.md new file mode 100644 index 0000000..ded29c9 --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/natRules/readme.md @@ -0,0 +1,51 @@ +# VPN Gateways NATRules `[Microsoft.Network/vpnGateways/natRules]` + +This module deploys VPN Gateways NATRules + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/vpnGateways/natRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnGateways/natRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `vpnGatewayName` | string | The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `externalMappings` | array | `[]` | | An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range. | +| `internalMappings` | array | `[]` | | An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range. | +| `ipConfigurationId` | string | `''` | | A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances. | +| `mode` | string | `''` | `['', EgressSnat, IngressSnat]` | The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway. | +| `type` | string | `''` | `['', Dynamic, Static]` | The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | +| `resourceGroupName` | string | The name of the resource group the NAT rule was deployed into. | +| `resourceId` | string | The resource ID of the NAT rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Network/vpnGateways/natRules/version.json b/modules/Microsoft.Network/vpnGateways/natRules/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/natRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/vpnGateways/readme.md b/modules/Microsoft.Network/vpnGateways/readme.md new file mode 100644 index 0000000..3827325 --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/readme.md @@ -0,0 +1,373 @@ +# VPN Gateways `[Microsoft.Network/vpnGateways]` + +This module deploys VPN Gateways. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Network/vpnGateways` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnGateways) | +| `Microsoft.Network/vpnGateways/natRules` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnGateways/natRules) | +| `Microsoft.Network/vpnGateways/vpnConnections` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnGateways/vpnConnections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the VPN gateway. | +| `virtualHubResourceId` | string | The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `bgpSettings` | object | `{object}` | | BGP settings details. | +| `connections` | _[connections](connections/readme.md)_ array | `[]` | | The connections to create in the VPN gateway. | +| `enableBgpRouteTranslationForNat` | bool | `False` | | Enable BGP routes translation for NAT on this VPN gateway. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `isRoutingPreferenceInternet` | bool | `False` | | Enable routing preference property for the public IP interface of the VPN gateway. | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natRules` | _[natRules](natRules/readme.md)_ array | `[]` | | List of all the NAT Rules to associate with the gateway. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vpnGatewayScaleUnit` | int | `2` | | The scale unit for this VPN gateway. | + + +### Parameter Usage: `bgpSettings` + +

+ +Parameter JSON format + +```json +"bgpSettings": { + "asn": 65515, + "peerWeight": 0, + "bgpPeeringAddresses": [ + { + "ipconfigurationId": "Instance0", + "defaultBgpIpAddresses": [ + "10.0.0.12" + ], + "customBgpIpAddresses": [], + "tunnelIpAddresses": [ + "20.84.35.53", + "10.0.0.4" + ] + }, + { + "ipconfigurationId": "Instance1", + "defaultBgpIpAddresses": [ + "10.0.0.13" + ], + "customBgpIpAddresses": [], + "tunnelIpAddresses": [ + "20.84.34.225", + "10.0.0.5" + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +bgpSettings: { + asn: 65515 + peerWeight: 0 + bgpPeeringAddresses: [ + { + ipconfigurationId: 'Instance0' + defaultBgpIpAddresses: [ + '10.0.0.12' + ] + customBgpIpAddresses: [] + tunnelIpAddresses: [ + '20.84.35.53' + '10.0.0.4' + ] + } + { + ipconfigurationId: 'Instance1' + defaultBgpIpAddresses: [ + '10.0.0.13' + ] + customBgpIpAddresses: [] + tunnelIpAddresses: [ + '20.84.34.225' + '10.0.0.5' + ] + } + ] +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the VPN gateway. | +| `resourceGroupName` | string | The name of the resource group the VPN gateway was deployed into. | +| `resourceId` | string | The resource ID of the VPN gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module vpnGateways './Microsoft.Network/vpnGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VpnGateways' + params: { + // Required parameters + name: '<>-az-vpngw-min-001' + virtualHubResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-min-001' + } +} +``` + +
+

+ +

+ +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": "<>-az-vpngw-min-001" + }, + "virtualHubResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module vpnGateways './Microsoft.Network/vpnGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VpnGateways' + params: { + // Required parameters + name: '<>-az-vpngw-x-001' + virtualHubResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001' + // Non-required parameters + bgpSettings: { + asn: 65515 + peerWeight: 0 + } + connections: [ + { + connectionBandwidth: 10 + enableBgp: true + name: 'Connection-<>-az-vsite-x-001' + remoteVpnSiteResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/vpnSites/<>-az-vsite-x-001' + routingConfiguration: { + associatedRouteTable: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable' + } + propagatedRouteTables: { + ids: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable' + } + ] + labels: [ + 'default' + ] + } + vnetRoutes: { + staticRoutes: [] + } + } + } + ] + lock: 'CanNotDelete' + natRules: [ + { + externalMappings: [ + { + addressSpace: '192.168.21.0/24' + } + ] + internalMappings: [ + { + addressSpace: '10.4.0.0/24' + } + ] + mode: 'EgressSnat' + name: 'natRule1' + type: 'Static' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-vpngw-x-001" + }, + "virtualHubResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001" + }, + // Non-required parameters + "bgpSettings": { + "value": { + "asn": 65515, + "peerWeight": 0 + } + }, + "connections": { + "value": [ + { + "connectionBandwidth": 10, + "enableBgp": true, + "name": "Connection-<>-az-vsite-x-001", + "remoteVpnSiteResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/vpnSites/<>-az-vsite-x-001", + "routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable" + }, + "propagatedRouteTables": { + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/<>-az-vhub-x-001/hubRouteTables/defaultRouteTable" + } + ], + "labels": [ + "default" + ] + }, + "vnetRoutes": { + "staticRoutes": [] + } + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "natRules": { + "value": [ + { + "externalMappings": [ + { + "addressSpace": "192.168.21.0/24" + } + ], + "internalMappings": [ + { + "addressSpace": "10.4.0.0/24" + } + ], + "mode": "EgressSnat", + "name": "natRule1", + "type": "Static" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/vpnGateways/version.json b/modules/Microsoft.Network/vpnGateways/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/vpnGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..82a3ef7 --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,37 @@ +param principalIds array +param principalType string = '' +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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + '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 vpnSite 'Microsoft.Network/vpnSites@2021-08-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(vpnSite.id, principalId, roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + } + scope: vpnSite +}] diff --git a/modules/Microsoft.Network/vpnSites/.test/min.parameters.json b/modules/Microsoft.Network/vpnSites/.test/min.parameters.json new file mode 100644 index 0000000..24791e0 --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/.test/min.parameters.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vSite-min-001" + }, + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "ipAddress": { + "value": "1.2.3.4" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001" + } + } +} diff --git a/modules/Microsoft.Network/vpnSites/.test/parameters.json b/modules/Microsoft.Network/vpnSites/.test/parameters.json new file mode 100644 index 0000000..94c534c --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/.test/parameters.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-vSite-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "tags": { + "value": { + "tagA": "valueA", + "tagB": "valueB" + } + }, + "deviceProperties": { + "value": { + "linkSpeedInMbps": 0 + } + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001" + }, + "vpnSiteLinks": { + "value": [ + { + "name": "<>-az-vSite-x-001", + "properties": { + "bgpProperties": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1" + }, + "ipAddress": "1.2.3.4", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + }, + { + "name": "Link1", + "properties": { + "bgpProperties": { + "asn": 65020, + "bgpPeeringAddress": "192.168.1.0" + }, + "ipAddress": "2.2.2.2", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + } + ] + }, + "o365Policy": { + "value": { + "breakOutCategories": { + "optimize": true, + "allow": true, + "default": true + } + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Network/vpnSites/deploy.bicep b/modules/Microsoft.Network/vpnSites/deploy.bicep new file mode 100644 index 0000000..980ad07 --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/deploy.bicep @@ -0,0 +1,108 @@ +@description('Required. Name of the VPN Site.') +param name string + +@description('Required. Resource ID of the virtual WAN to link to.') +param virtualWanId string + +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. An array of IP address ranges that can be used by subnets of the virtual network. Must be provided if no bgpProperties or VPNSiteLinks are configured.') +param addressPrefixes array = [] + +@description('Optional. BGP settings details. Must be provided if no addressPrefixes or VPNSiteLinks are configured. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead.') +param bgpProperties object = {} + +@description('Optional. List of properties of the device.') +param deviceProperties object = {} + +@description('Optional. The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead.') +param ipAddress string = '' + +@description('Optional. IsSecuritySite flag.') +param isSecuritySite bool = false + +@description('Optional. The Office365 breakout policy.') +param o365Policy object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. List of all VPN site links.') +param vpnSiteLinks array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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 = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnSite 'Microsoft.Network/vpnSites@2021-08-01' = { + name: name + location: location + tags: tags + properties: { + addressSpace: !empty(addressPrefixes) ? { + addressPrefixes: addressPrefixes + } : null + bgpProperties: !empty(bgpProperties) ? bgpProperties : null + deviceProperties: !empty(deviceProperties) ? deviceProperties : null + ipAddress: !empty(ipAddress) ? ipAddress : null + isSecuritySite: isSecuritySite + o365Policy: !empty(o365Policy) ? o365Policy : null + virtualWan: { + id: virtualWanId + } + vpnSiteLinks: !empty(vpnSiteLinks) ? vpnSiteLinks : null + } +} + +resource vpnSite_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${vpnSite.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vpnSite +} + +module vpnSite_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VWan-Rbac-${index}' + params: { + principalIds: roleAssignment.principalIds + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: vpnSite.id + } +}] + +@description('The name of the VPN site.') +output name string = vpnSite.name + +@description('The resource ID of the VPN site.') +output resourceId string = vpnSite.id + +@description('The resource group the VPN site was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = vpnSite.location diff --git a/modules/Microsoft.Network/vpnSites/readme.md b/modules/Microsoft.Network/vpnSites/readme.md new file mode 100644 index 0000000..327ddb9 --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/readme.md @@ -0,0 +1,549 @@ +# VPN Sites `[Microsoft.Network/vpnSites]` + +This module deploys a VPN Site. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/vpnSites` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/vpnSites) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the VPN Site. | +| `virtualWanId` | string | Resource ID of the virtual WAN to link to. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `addressPrefixes` | array | `[]` | | An array of IP address ranges that can be used by subnets of the virtual network. Must be provided if no bgpProperties or VPNSiteLinks are configured. | +| `bgpProperties` | object | `{object}` | | BGP settings details. Must be provided if no addressPrefixes or VPNSiteLinks are configured. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. | +| `deviceProperties` | object | `{object}` | | List of properties of the device. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ipAddress` | string | `''` | | The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. | +| `isSecuritySite` | bool | `False` | | IsSecuritySite flag. | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `o365Policy` | object | `{object}` | | The Office365 breakout policy. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the resource. | +| `vpnSiteLinks` | array | `[]` | | List of all VPN site links. | + + +### Parameter Usage `o365Policy` + +

+ +Parameter JSON format + +```json +"o365Policy": { + "value": { + "breakOutCategories": { + "optimize": true, + "allow": true, + "default": true + } + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +o365Policy: { + breakOutCategories: { + optimize: true + allow: true + default: true + } +} +``` + +
+

+ +### Parameter Usage `deviceProperties` + +

+ +Parameter JSON format + +```json +"deviceProperties": { + "value": { + "deviceModel": "morty", + "deviceVendor": "contoso", + "linkSpeedInMbps": 0 + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +deviceProperties: { + deviceModel: 'morty' + deviceVendor: 'contoso' + linkSpeedInMbps: 0 +} +``` + +
+

+ +### Parameter Usage `bgpProperties` + +The BGP properties. Note: This is a deprecated property, please use the corresponding `VpnSiteLinks` property instead. + +

+ +Parameter JSON format + +```json +"bgpProperties": { + "value": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1", + "peerWeight": 0 + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + peerWeight: 0 +} +``` + +
+

+ +### Parameter Usage `vpnSiteLinks` + +An array of links. Should be used instead of the top-level `ipAddress` & `bgpProperties` properties. If using links, one default link with same name and properties as VpnSite itself is mandatory. + +

+ +Parameter JSON format + +```json +"vpnSiteLinks": { + "value": [ + { + "name": "<>-az-vSite-x-001", + "properties": { + "bgpProperties": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1" + }, + "ipAddress": "1.2.3.4", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +vpnSiteLinks: [ + { + name: '<>-az-vSite-x-001' + properties: { + bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + } + ipAddress: '1.2.3.4' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the VPN site. | +| `resourceGroupName` | string | The resource group the VPN site was deployed into. | +| `resourceId` | string | The resource ID of the VPN site. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module vpnSites './Microsoft.Network/vpnSites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VpnSites' + params: { + // Required parameters + name: '<>-az-vSite-min-001' + virtualWanId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001' + // Non-required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + ipAddress: '1.2.3.4' + } +} +``` + +
+

+ +

+ +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": "<>-az-vSite-min-001" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001" + }, + // Non-required parameters + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "ipAddress": { + "value": "1.2.3.4" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module vpnSites './Microsoft.Network/vpnSites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-VpnSites' + params: { + // Required parameters + name: '<>-az-vSite-x-001' + virtualWanId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001' + // Non-required parameters + deviceProperties: { + linkSpeedInMbps: 0 + } + lock: 'CanNotDelete' + o365Policy: { + breakOutCategories: { + allow: true + default: true + optimize: true + } + } + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + tagA: 'valueA' + tagB: 'valueB' + } + vpnSiteLinks: [ + { + name: '<>-az-vSite-x-001' + properties: { + bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + } + ipAddress: '1.2.3.4' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + { + name: 'Link1' + properties: { + bgpProperties: { + asn: 65020 + bgpPeeringAddress: '192.168.1.0' + } + ipAddress: '2.2.2.2' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-vSite-x-001" + }, + "virtualWanId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualWans/apd-<>-az-vw-x-001" + }, + // Non-required parameters + "deviceProperties": { + "value": { + "linkSpeedInMbps": 0 + } + }, + "lock": { + "value": "CanNotDelete" + }, + "o365Policy": { + "value": { + "breakOutCategories": { + "allow": true, + "default": true, + "optimize": true + } + } + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "tagA": "valueA", + "tagB": "valueB" + } + }, + "vpnSiteLinks": { + "value": [ + { + "name": "<>-az-vSite-x-001", + "properties": { + "bgpProperties": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1" + }, + "ipAddress": "1.2.3.4", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + }, + { + "name": "Link1", + "properties": { + "bgpProperties": { + "asn": 65020, + "bgpPeeringAddress": "192.168.1.0" + }, + "ipAddress": "2.2.2.2", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Network/vpnSites/version.json b/modules/Microsoft.Network/vpnSites/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Network/vpnSites/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..5906dac --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Azure Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Azure Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Azure Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + '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') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(logAnalyticsWorkspace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: logAnalyticsWorkspace +}] diff --git a/modules/Microsoft.OperationalInsights/workspaces/.test/min.parameters.json b/modules/Microsoft.OperationalInsights/workspaces/.test/min.parameters.json new file mode 100644 index 0000000..97fc2fa --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/.test/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": "<>-az-law-min-001" + } + } +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/.test/parameters.json b/modules/Microsoft.OperationalInsights/workspaces/.test/parameters.json new file mode 100644 index 0000000..462dcfb --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/.test/parameters.json @@ -0,0 +1,179 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "publicNetworkAccessForIngestion": { + "value": "Disabled" + }, + "publicNetworkAccessForQuery": { + "value": "Disabled" + }, + "dailyQuotaGb": { + "value": 10 + }, + "storageInsightsConfigs": { + "value": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001", + "tables": [ + "WADWindowsEventLogsTable", + "WADETWEventTable", + "WADServiceFabric*EventTable", + "LinuxsyslogVer2v0" + ] + } + ] + }, + "linkedServices": { + "value": [ + { + "name": "Automation", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Automation/automationAccounts/adp-<>-az-aut-x-001" + } + ] + }, + "linkedStorageAccounts": { + "value": [ + { + "name": "Query", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001" + } + ] + }, + "savedSearches": { + "value": [ + { + "name": "VMSSQueries", + "displayName": "VMSS Instance Count2", + "category": "VDC Saved Searches", + "query": "Event | where Source == 'ServiceFabricNodeBootstrapAgent' | summarize AggregatedValue = count() by Computer" + } + ] + }, + "dataSources": { + "value": [ + { + "name": "applicationEvent", + "kind": "WindowsEvent", + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + }, + { + "eventType": "Information" + } + ] + }, + { + "name": "windowsPerfCounter1", + "kind": "WindowsPerformanceCounter", + "objectName": "Processor", + "instanceName": "*", + "intervalSeconds": 60, + "counterName": "% Processor Time" + }, + { + "name": "sampleIISLog1", + "kind": "IISLogs", + "state": "OnPremiseEnabled" + }, + { + "name": "sampleSyslog1", + "kind": "LinuxSyslog", + "syslogName": "kern", + "syslogSeverities": [ + { + "severity": "emerg" + }, + { + "severity": "alert" + }, + { + "severity": "crit" + }, + { + "severity": "err" + }, + { + "severity": "warning" + } + ] + }, + { + "name": "sampleSyslogCollection1", + "kind": "LinuxSyslogCollection", + "state": "Enabled" + }, + { + "name": "sampleLinuxPerf1", + "kind": "LinuxPerformanceObject", + "syslogSeverities": [ + { + "counterName": "% Used Inodes" + }, + { + "counterName": "Free Megabytes" + }, + { + "counterName": "% Used Space" + }, + { + "counterName": "Disk Transfers/sec" + }, + { + "counterName": "Disk Reads/sec" + }, + { + "counterName": "Disk Writes/sec" + } + ], + "objectName": "Logical Disk", + "instanceName": "*", + "intervalSeconds": 10 + }, + { + "name": "sampleLinuxPerfCollection1", + "kind": "LinuxPerformanceCollection", + "state": "Enabled" + } + ] + }, + "gallerySolutions": { + "value": [ + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "useResourcePermissions": { + "value": true + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep new file mode 100644 index 0000000..9ecbbcb --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep @@ -0,0 +1,102 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the solution.') +param name string + +@description('Required. The kind of the DataSource.') +@allowed([ + 'AzureActivityLog' + 'WindowsEvent' + 'WindowsPerformanceCounter' + 'IISLogs' + 'LinuxSyslog' + 'LinuxSyslogCollection' + 'LinuxPerformanceObject' + 'LinuxPerformanceCollection' +]) +param kind string = 'AzureActivityLog' + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Resource ID of the resource to be linked.') +param linkedResourceId string = '' + +@description('Optional. Windows event log name to configure when kind is WindowsEvent.') +param eventLogName string = '' + +@description('Optional. Windows event types to configure when kind is WindowsEvent.') +param eventTypes array = [] + +@description('Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param objectName string = '' + +@description('Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param instanceName string = '*' + +@description('Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param intervalSeconds int = 60 + +@description('Optional. List of counters to configure when the kind is LinuxPerformanceObject.') +param performanceCounters array = [] + +@description('Optional. Counter name to configure when kind is WindowsPerformanceCounter.') +param counterName string = '' + +@description('Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection.') +param state string = '' + +@description('Optional. System log to configure when kind is LinuxSyslog.') +param syslogName string = '' + +@description('Optional. Severities to configure when kind is LinuxSyslog.') +param syslogSeverities array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource dataSource 'Microsoft.OperationalInsights/workspaces/dataSources@2020-08-01' = { + name: name + parent: workspace + kind: kind + tags: tags + properties: { + linkedResourceId: !empty(kind) && kind == 'AzureActivityLog' ? linkedResourceId : null + eventLogName: !empty(kind) && kind == 'WindowsEvent' ? eventLogName : null + eventTypes: !empty(kind) && kind == 'WindowsEvent' ? eventTypes : null + objectName: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? objectName : null + instanceName: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? instanceName : null + intervalSeconds: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? intervalSeconds : null + counterName: !empty(kind) && kind == 'WindowsPerformanceCounter' ? counterName : null + state: !empty(kind) && (kind == 'IISLogs' || kind == 'LinuxSyslogCollection' || kind == 'LinuxPerformanceCollection') ? state : null + syslogName: !empty(kind) && kind == 'LinuxSyslog' ? syslogName : null + syslogSeverities: !empty(kind) && (kind == 'LinuxSyslog' || kind == 'LinuxPerformanceObject') ? syslogSeverities : null + performanceCounters: !empty(kind) && kind == 'LinuxPerformanceObject' ? performanceCounters : null + } +} + +@description('The resource ID of the deployed data source.') +output resourceId string = dataSource.id + +@description('The resource group where the data source is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed data source.') +output name string = dataSource.name diff --git a/modules/Microsoft.OperationalInsights/workspaces/dataSources/readme.md b/modules/Microsoft.OperationalInsights/workspaces/dataSources/readme.md new file mode 100644 index 0000000..0da03c5 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/dataSources/readme.md @@ -0,0 +1,100 @@ +# Operationalinsights Workspaces Datasources `[Microsoft.OperationalInsights/workspaces/dataSources]` + +This template deploys a data source for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `kind` | string | `'AzureActivityLog'` | `[AzureActivityLog, IISLogs, LinuxPerformanceCollection, LinuxPerformanceObject, LinuxSyslog, LinuxSyslogCollection, WindowsEvent, WindowsPerformanceCounter]` | The kind of the DataSource. | +| `name` | string | | | Name of the solution. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `counterName` | string | `''` | Counter name to configure when kind is WindowsPerformanceCounter. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `eventLogName` | string | `''` | Windows event log name to configure when kind is WindowsEvent. | +| `eventTypes` | array | `[]` | Windows event types to configure when kind is WindowsEvent. | +| `instanceName` | string | `'*'` | Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `intervalSeconds` | int | `60` | Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `linkedResourceId` | string | `''` | Resource ID of the resource to be linked. | +| `objectName` | string | `''` | Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `performanceCounters` | array | `[]` | List of counters to configure when the kind is LinuxPerformanceObject. | +| `state` | string | `''` | State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection. | +| `syslogName` | string | `''` | System log to configure when kind is LinuxSyslog. | +| `syslogSeverities` | array | `[]` | Severities to configure when kind is LinuxSyslog. | +| `tags` | object | `{object}` | Tags to configure in the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed data source. | +| `resourceGroupName` | string | The resource group where the data source is deployed. | +| `resourceId` | string | The resource ID of the deployed data source. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.OperationalInsights/workspaces/dataSources/version.json b/modules/Microsoft.OperationalInsights/workspaces/dataSources/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/dataSources/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/deploy.bicep new file mode 100644 index 0000000..c601cd2 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/deploy.bicep @@ -0,0 +1,305 @@ +@description('Required. Name of the Log Analytics workspace.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Service Tier: PerGB2018, Free, Standalone, PerGB or PerNode.') +@allowed([ + 'Free' + 'Standalone' + 'PerNode' + 'PerGB2018' +]) +param serviceTier string = 'PerGB2018' + +@description('Optional. List of storage accounts to be read by the workspace.') +param storageInsightsConfigs array = [] + +@description('Optional. List of services to be linked.') +param linkedServices array = [] + +@description('Conditional. List of Storage Accounts to be linked. Required if \'forceCmkForQuery\' is set to \'true\' and \'savedSearches\' is not empty.') +param linkedStorageAccounts array = [] + +@description('Optional. Kusto Query Language searches to save.') +param savedSearches array = [] + +@description('Optional. LAW data sources to configure.') +param dataSources array = [] + +@description('Optional. List of gallerySolutions to be created in the log analytics workspace.') +param gallerySolutions array = [] + +@description('Optional. Number of days data will be retained for.') +@minValue(0) +@maxValue(730) +param dataRetention int = 365 + +@description('Optional. The workspace daily quota for ingestion.') +@minValue(-1) +param dailyQuotaGb int = -1 + +@description('Optional. The network access type for accessing Log Analytics ingestion.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForIngestion string = 'Enabled' + +@description('Optional. The network access type for accessing Log Analytics query.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForQuery string = 'Enabled' + +@description('Optional. Set to \'true\' to use resource or workspace permissions and \'false\' (or leave empty) to require workspace permissions.') +param useResourcePermissions bool = false + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Indicates whether customer managed storage is mandatory for query management.') +param forceCmkForQuery bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'Audit' +]) +param diagnosticLogCategoriesToEnable array = [ + 'Audit' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var logAnalyticsSearchVersion = 1 + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + location: location + name: name + tags: tags + properties: { + features: { + searchVersion: logAnalyticsSearchVersion + enableLogAccessUsingOnlyResourcePermissions: useResourcePermissions + } + sku: { + name: serviceTier + } + retentionInDays: dataRetention + workspaceCapping: { + dailyQuotaGb: dailyQuotaGb + } + publicNetworkAccessForIngestion: publicNetworkAccessForIngestion + publicNetworkAccessForQuery: publicNetworkAccessForQuery + forceCmkForQuery: forceCmkForQuery + } +} + +resource logAnalyticsWorkspace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: logAnalyticsWorkspace +} + +module logAnalyticsWorkspace_storageInsightConfigs 'storageInsightConfigs/deploy.bicep' = [for (storageInsightsConfig, index) in storageInsightsConfigs: { + name: '${uniqueString(deployment().name, location)}-LAW-StorageInsightsConfig-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + containers: contains(storageInsightsConfig, 'containers') ? storageInsightsConfig.containers : [] + tables: contains(storageInsightsConfig, 'tables') ? storageInsightsConfig.tables : [] + storageAccountId: storageInsightsConfig.storageAccountId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_linkedServices 'linkedServices/deploy.bicep' = [for (linkedService, index) in linkedServices: { + name: '${uniqueString(deployment().name, location)}-LAW-LinkedService-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: linkedService.name + resourceId: contains(linkedService, 'resourceId') ? linkedService.resourceId : '' + writeAccessResourceId: contains(linkedService, 'writeAccessResourceId') ? linkedService.writeAccessResourceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_linkedStorageAccounts 'linkedStorageAccounts/deploy.bicep' = [for (linkedStorageAccount, index) in linkedStorageAccounts: { + name: '${uniqueString(deployment().name, location)}-LAW-LinkedStorageAccount-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: linkedStorageAccount.name + resourceId: linkedStorageAccount.resourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_savedSearches 'savedSearches/deploy.bicep' = [for (savedSearch, index) in savedSearches: { + name: '${uniqueString(deployment().name, location)}-LAW-SavedSearch-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: '${savedSearch.name}${uniqueString(deployment().name)}' + etag: contains(savedSearch, 'eTag') ? savedSearch.etag : '*' + displayName: savedSearch.displayName + category: savedSearch.category + query: savedSearch.query + functionAlias: contains(savedSearch, 'functionAlias') ? savedSearch.functionAlias : '' + functionParameters: contains(savedSearch, 'functionParameters') ? savedSearch.functionParameters : '' + version: contains(savedSearch, 'version') ? savedSearch.version : 2 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + logAnalyticsWorkspace_linkedStorageAccounts + ] +}] + +module logAnalyticsWorkspace_dataSources 'dataSources/deploy.bicep' = [for (dataSource, index) in dataSources: { + name: '${uniqueString(deployment().name, location)}-LAW-DataSource-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: dataSource.name + kind: dataSource.kind + linkedResourceId: contains(dataSource, 'linkedResourceId') ? dataSource.linkedResourceId : '' + eventLogName: contains(dataSource, 'eventLogName') ? dataSource.eventLogName : '' + eventTypes: contains(dataSource, 'eventTypes') ? dataSource.eventTypes : [] + objectName: contains(dataSource, 'objectName') ? dataSource.objectName : '' + instanceName: contains(dataSource, 'instanceName') ? dataSource.instanceName : '' + intervalSeconds: contains(dataSource, 'intervalSeconds') ? dataSource.intervalSeconds : 60 + counterName: contains(dataSource, 'counterName') ? dataSource.counterName : '' + state: contains(dataSource, 'state') ? dataSource.state : '' + syslogName: contains(dataSource, 'syslogName') ? dataSource.syslogName : '' + syslogSeverities: contains(dataSource, 'syslogSeverities') ? dataSource.syslogSeverities : [] + performanceCounters: contains(dataSource, 'performanceCounters') ? dataSource.performanceCounters : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_solutions '../../Microsoft.OperationsManagement/solutions/deploy.bicep' = [for (gallerySolution, index) in gallerySolutions: if (!empty(gallerySolutions)) { + name: '${uniqueString(deployment().name, location)}-LAW-Solution-${index}' + params: { + name: gallerySolution.name + location: location + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + product: contains(gallerySolution, 'product') ? gallerySolution.product : 'OMSGallery' + publisher: contains(gallerySolution, 'publisher') ? gallerySolution.publisher : 'Microsoft' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource logAnalyticsWorkspace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${logAnalyticsWorkspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: logAnalyticsWorkspace +} + +module logAnalyticsWorkspace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LAW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: logAnalyticsWorkspace.id + } +}] + +@description('The resource ID of the deployed log analytics workspace.') +output resourceId string = logAnalyticsWorkspace.id + +@description('The resource group of the deployed log analytics workspace.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed log analytics workspace.') +output name string = logAnalyticsWorkspace.name + +@description('The ID associated with the workspace.') +output logAnalyticsWorkspaceId string = logAnalyticsWorkspace.properties.customerId + +@description('The location the resource was deployed into.') +output location string = logAnalyticsWorkspace.location diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep new file mode 100644 index 0000000..73f552c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep @@ -0,0 +1,52 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the link.') +param name string + +@description('Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access.') +param resourceId string = '' + +@description('Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access.') +param writeAccessResourceId string = '' + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource linkedService 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = { + name: name + parent: workspace + tags: tags + properties: { + resourceId: resourceId + writeAccessResourceId: empty(writeAccessResourceId) ? null : writeAccessResourceId + } +} + +@description('The name of the deployed linked service.') +output name string = linkedService.name + +@description('The resource ID of the deployed linked service.') +output resourceId string = linkedService.id + +@description('The resource group where the linked service is deployed.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md new file mode 100644 index 0000000..6fb2f4d --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md @@ -0,0 +1,90 @@ +# Operationalinsights Workspaces Linked Services `[Microsoft.OperationalInsights/workspaces/linkedServices]` + +This template deploys a linked service for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Name of the link. | +| `resourceId` | string | `''` | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `tags` | object | `{object}` | Tags to configure in the resource. | +| `writeAccessResourceId` | string | `''` | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed linked service. | +| `resourceGroupName` | string | The resource group where the linked service is deployed. | +| `resourceId` | string | The resource ID of the deployed linked service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedServices/version.json b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep new file mode 100644 index 0000000..a2b5de4 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep @@ -0,0 +1,52 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the link.') +@allowed([ + 'Query' + 'Alerts' + 'CustomLogs' + 'AzureWatson' +]) +param name string + +@description('Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access.') +param resourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource linkedStorageAccount 'Microsoft.OperationalInsights/workspaces/linkedStorageAccounts@2020-08-01' = { + name: name + parent: workspace + properties: { + storageAccountIds: [ + resourceId + ] + } +} + +@description('The name of the deployed linked storage account.') +output name string = linkedStorageAccount.name + +@description('The resource ID of the deployed linked storage account.') +output resourceId string = linkedStorageAccount.id + +@description('The resource group where the linked storage account is deployed.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md new file mode 100644 index 0000000..3d238d9 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md @@ -0,0 +1,47 @@ +# Operational Insights Workspaces Linked Storage Accounts `[Microsoft.OperationalInsights/workspaces/linkedStorageAccounts]` + +This template deploys a linked Storage Accounts for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `[Alerts, AzureWatson, CustomLogs, Query]` | Name of the link. | +| `resourceId` | string | | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed linked storage account. | +| `resourceGroupName` | string | The resource group where the linked storage account is deployed. | +| `resourceId` | string | The resource ID of the deployed linked storage account. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/readme.md b/modules/Microsoft.OperationalInsights/workspaces/readme.md new file mode 100644 index 0000000..824f94c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/readme.md @@ -0,0 +1,848 @@ +# Log Analytics Workspaces `[Microsoft.OperationalInsights/workspaces]` + +This template deploys a log analytics workspace. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.OperationalInsights/workspaces` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2021-06-01/workspaces) | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Log Analytics workspace. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `linkedStorageAccounts` | _[linkedStorageAccounts](linkedStorageAccounts/readme.md)_ array | List of Storage Accounts to be linked. Required if 'forceCmkForQuery' is set to 'true' and 'savedSearches' is not empty. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `dailyQuotaGb` | int | `-1` | | The workspace daily quota for ingestion. | +| `dataRetention` | int | `365` | | Number of days data will be retained for. | +| `dataSources` | _[dataSources](dataSources/readme.md)_ array | `[]` | | LAW data sources to configure. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Audit]` | `[Audit]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `forceCmkForQuery` | bool | `True` | | Indicates whether customer managed storage is mandatory for query management. | +| `gallerySolutions` | array | `[]` | | List of gallerySolutions to be created in the log analytics workspace. | +| `linkedServices` | _[linkedServices](linkedServices/readme.md)_ array | `[]` | | List of services to be linked. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicNetworkAccessForIngestion` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Log Analytics ingestion. | +| `publicNetworkAccessForQuery` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Log Analytics query. | +| `roleAssignments` | array | `[]` | | 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'. | +| `savedSearches` | _[savedSearches](savedSearches/readme.md)_ array | `[]` | | Kusto Query Language searches to save. | +| `serviceTier` | string | `'PerGB2018'` | `[Free, PerGB2018, PerNode, Standalone]` | Service Tier: PerGB2018, Free, Standalone, PerGB or PerNode. | +| `storageInsightsConfigs` | array | `[]` | | List of storage accounts to be read by the workspace. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `useResourcePermissions` | bool | `False` | | Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. | + + +### Parameter Usage: `gallerySolutions` + +Ref cross-referenced _[solutions](../../Microsoft.OperationsManagement/solutions/readme.md)_ + +

+ +Parameter JSON format + +```json +"gallerySolutions": { + "value": [ + { + "name": "AgentHealthAssessment", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AlertManagement", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AntiMalware", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureActivity", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureCdnCoreAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureDataFactoryAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureNSGAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureSQLAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ChangeTracking", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Containers", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "InfrastructureInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "KeyVaultAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "LogicAppsManagement", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "NetworkMonitoring", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Security", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "SecurityCenterFree", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ServiceFabric", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ServiceMap", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "SQLAssessment", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Updates", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "VMInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "WireData2", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "WaaSUpdateInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +gallerySolutions: [ + { + name: 'AgentHealthAssessment' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AlertManagement' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AntiMalware' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureActivity' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureCdnCoreAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureDataFactoryAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureNSGAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureSQLAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ChangeTracking' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Containers' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'InfrastructureInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'KeyVaultAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'LogicAppsManagement' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'NetworkMonitoring' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Security' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'SecurityCenterFree' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ServiceFabric' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ServiceMap' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'SQLAssessment' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'VMInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'WireData2' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'WaaSUpdateInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `logAnalyticsWorkspaceId` | string | The ID associated with the workspace. | +| `name` | string | The name of the deployed log analytics workspace. | +| `resourceGroupName` | string | The resource group of the deployed log analytics workspace. | +| `resourceId` | string | The resource ID of the deployed log analytics workspace. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.OperationsManagement/solutions` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.OperationalInsights/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + name: '<>-az-law-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-law-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.OperationalInsights/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + name: '<>-az-law-x-001' + // Non-required parameters + dailyQuotaGb: 10 + dataSources: [ + { + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + { + kind: 'LinuxSyslog' + name: 'sampleSyslog1' + syslogName: 'kern' + syslogSeverities: [ + { + severity: 'emerg' + } + { + severity: 'alert' + } + { + severity: 'crit' + } + { + severity: 'err' + } + { + severity: 'warning' + } + ] + } + { + kind: 'LinuxSyslogCollection' + name: 'sampleSyslogCollection1' + state: 'Enabled' + } + { + instanceName: '*' + intervalSeconds: 10 + kind: 'LinuxPerformanceObject' + name: 'sampleLinuxPerf1' + objectName: 'Logical Disk' + syslogSeverities: [ + { + counterName: '% Used Inodes' + } + { + counterName: 'Free Megabytes' + } + { + counterName: '% Used Space' + } + { + counterName: 'Disk Transfers/sec' + } + { + counterName: 'Disk Reads/sec' + } + { + counterName: 'Disk Writes/sec' + } + ] + } + { + kind: 'LinuxPerformanceCollection' + name: 'sampleLinuxPerfCollection1' + state: 'Enabled' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + gallerySolutions: [ + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + linkedServices: [ + { + name: 'Automation' + resourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Automation/automationAccounts/adp-<>-az-aut-x-001' + } + ] + linkedStorageAccounts: [ + { + name: 'Query' + resourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001' + } + ] + lock: 'CanNotDelete' + publicNetworkAccessForIngestion: 'Disabled' + publicNetworkAccessForQuery: 'Disabled' + savedSearches: [ + { + category: 'VDC Saved Searches' + displayName: 'VMSS Instance Count2' + name: 'VMSSQueries' + query: 'Event | where Source == 'ServiceFabricNodeBootstrapAgent' | summarize AggregatedValue = count() by Computer' + } + ] + storageInsightsConfigs: [ + { + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001' + tables: [ + 'LinuxsyslogVer2v0' + 'WADETWEventTable' + 'WADServiceFabric*EventTable' + 'WADWindowsEventLogsTable' + ] + } + ] + useResourcePermissions: true + } +} +``` + +
+

+ +

+ +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": "<>-az-law-x-001" + }, + // Non-required parameters + "dailyQuotaGb": { + "value": 10 + }, + "dataSources": { + "value": [ + { + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + }, + { + "eventType": "Information" + } + ], + "kind": "WindowsEvent", + "name": "applicationEvent" + }, + { + "counterName": "% Processor Time", + "instanceName": "*", + "intervalSeconds": 60, + "kind": "WindowsPerformanceCounter", + "name": "windowsPerfCounter1", + "objectName": "Processor" + }, + { + "kind": "IISLogs", + "name": "sampleIISLog1", + "state": "OnPremiseEnabled" + }, + { + "kind": "LinuxSyslog", + "name": "sampleSyslog1", + "syslogName": "kern", + "syslogSeverities": [ + { + "severity": "emerg" + }, + { + "severity": "alert" + }, + { + "severity": "crit" + }, + { + "severity": "err" + }, + { + "severity": "warning" + } + ] + }, + { + "kind": "LinuxSyslogCollection", + "name": "sampleSyslogCollection1", + "state": "Enabled" + }, + { + "instanceName": "*", + "intervalSeconds": 10, + "kind": "LinuxPerformanceObject", + "name": "sampleLinuxPerf1", + "objectName": "Logical Disk", + "syslogSeverities": [ + { + "counterName": "% Used Inodes" + }, + { + "counterName": "Free Megabytes" + }, + { + "counterName": "% Used Space" + }, + { + "counterName": "Disk Transfers/sec" + }, + { + "counterName": "Disk Reads/sec" + }, + { + "counterName": "Disk Writes/sec" + } + ] + }, + { + "kind": "LinuxPerformanceCollection", + "name": "sampleLinuxPerfCollection1", + "state": "Enabled" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "gallerySolutions": { + "value": [ + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "linkedServices": { + "value": [ + { + "name": "Automation", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Automation/automationAccounts/adp-<>-az-aut-x-001" + } + ] + }, + "linkedStorageAccounts": { + "value": [ + { + "name": "Query", + "resourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "publicNetworkAccessForIngestion": { + "value": "Disabled" + }, + "publicNetworkAccessForQuery": { + "value": "Disabled" + }, + "savedSearches": { + "value": [ + { + "category": "VDC Saved Searches", + "displayName": "VMSS Instance Count2", + "name": "VMSSQueries", + "query": "Event | where Source == 'ServiceFabricNodeBootstrapAgent' | summarize AggregatedValue = count() by Computer" + } + ] + }, + "storageInsightsConfigs": { + "value": [ + { + "storageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001", + "tables": [ + "LinuxsyslogVer2v0", + "WADETWEventTable", + "WADServiceFabric*EventTable", + "WADWindowsEventLogsTable" + ] + } + ] + }, + "useResourcePermissions": { + "value": true + } + } +} +``` + +
+

diff --git a/modules/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep new file mode 100644 index 0000000..aed7b98 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep @@ -0,0 +1,73 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the saved search.') +param name string + +@description('Required. Display name for the search.') +param displayName string + +@description('Required. Query category.') +param category string + +@description('Required. Kusto Query to be stored.') +param query string + +@description('Optional. Tags to configure in the resource.') +param tags array = [] + +@description('Optional. The function alias if query serves as a function.') +param functionAlias string = '' + +@description('Optional. The optional function parameters if query serves as a function. Value should be in the following format: "param-name1:type1 = default_value1, param-name2:type2 = default_value2". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions.') +param functionParameters string = '' + +@description('Optional. The version number of the query language.') +param version int = 2 + +@description('Optional. The ETag of the saved search. To override an existing saved search, use "*" or specify the current Etag.') +param etag string = '*' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource savedSearch 'Microsoft.OperationalInsights/workspaces/savedSearches@2020-08-01' = { + name: name + parent: workspace + //etag: etag // According to API, the variable should be here, but it doesn't work here. + properties: { + etag: etag + tags: tags + displayName: displayName + category: category + query: query + functionAlias: functionAlias + functionParameters: functionParameters + version: version + } +} + +@description('The resource ID of the deployed saved search.') +output resourceId string = savedSearch.id + +@description('The resource group where the saved search is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed saved search.') +output name string = savedSearch.name diff --git a/modules/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md new file mode 100644 index 0000000..e0d8716 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md @@ -0,0 +1,95 @@ +# Operationalinsights Workspaces Saved Searches `[Microsoft.OperationalInsights/workspaces/savedSearches]` + +This template deploys a saved search for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `category` | string | Query category. | +| `displayName` | string | Display name for the search. | +| `name` | string | Name of the saved search. | +| `query` | string | Kusto Query to be stored. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `etag` | string | `'*'` | The ETag of the saved search. To override an existing saved search, use "*" or specify the current Etag. | +| `functionAlias` | string | `''` | The function alias if query serves as a function. | +| `functionParameters` | string | `''` | The optional function parameters if query serves as a function. Value should be in the following format: "param-name1:type1 = default_value1, param-name2:type2 = default_value2". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions. | +| `tags` | array | `[]` | Tags to configure in the resource. | +| `version` | int | `2` | The version number of the query language. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed saved search. | +| `resourceGroupName` | string | The resource group where the saved search is deployed. | +| `resourceId` | string | The resource ID of the deployed saved search. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.OperationalInsights/workspaces/savedSearches/version.json b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/savedSearches/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep new file mode 100644 index 0000000..4ded2de --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep @@ -0,0 +1,63 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Optional. The name of the storage insights config.') +param name string = '${last(split(storageAccountId, '/'))}-stinsconfig' + +@description('Required. The Azure Resource Manager ID of the storage account resource.') +param storageAccountId string + +@description('Optional. The names of the blob containers that the workspace should read.') +param containers array = [] + +@description('Optional. The names of the Azure tables that the workspace should read.') +param tables array = [] + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' existing = { + name: last(split(storageAccountId, '/')) +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource storageinsightconfig 'Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2020-08-01' = { + name: name + parent: workspace + tags: tags + properties: { + containers: containers + tables: tables + storageAccount: { + id: storageAccountId + key: storageAccount.listKeys().keys[0].value + } + } +} + +@description('The resource ID of the deployed storage insights configuration.') +output resourceId string = storageinsightconfig.id + +@description('The resource group where the storage insight configuration is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the storage insights configuration.') +output name string = storageinsightconfig.name diff --git a/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md new file mode 100644 index 0000000..ec086b8 --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md @@ -0,0 +1,91 @@ +# Operationalinsights Workspaces Storage Insight Configs `[Microsoft.OperationalInsights/workspaces/storageInsightConfigs]` + +This template deploys a storage insights configuration for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountId` | string | The Azure Resource Manager ID of the storage account resource. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `containers` | array | `[]` | The names of the blob containers that the workspace should read. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `[format('{0}-stinsconfig', last(split(parameters('storageAccountId'), '/')))]` | The name of the storage insights config. | +| `tables` | array | `[]` | The names of the Azure tables that the workspace should read. | +| `tags` | object | `{object}` | Tags to configure in the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the storage insights configuration. | +| `resourceGroupName` | string | The resource group where the storage insight configuration is deployed. | +| `resourceId` | string | The resource ID of the deployed storage insights configuration. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationalInsights/workspaces/version.json b/modules/Microsoft.OperationalInsights/workspaces/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.OperationalInsights/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.OperationsManagement/solutions/.test/min.parameters.json b/modules/Microsoft.OperationsManagement/solutions/.test/min.parameters.json new file mode 100644 index 0000000..6844bb4 --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "Updates" + }, + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + } + } +} diff --git a/modules/Microsoft.OperationsManagement/solutions/.test/ms.parameters.json b/modules/Microsoft.OperationsManagement/solutions/.test/ms.parameters.json new file mode 100644 index 0000000..c7dcb66 --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/.test/ms.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "AzureAutomation" + }, + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + }, + "product": { + "value": "OMSGallery" + }, + "publisher": { + "value": "Microsoft" + } + } +} diff --git a/modules/Microsoft.OperationsManagement/solutions/.test/nonms.parameters.json b/modules/Microsoft.OperationsManagement/solutions/.test/nonms.parameters.json new file mode 100644 index 0000000..a040bf8 --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/.test/nonms.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "nonmsTestSolution" + }, + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + }, + "product": { + "value": "nonmsTestSolutionProduct" + }, + "publisher": { + "value": "nonmsTestSolutionPublisher" + } + } +} diff --git a/modules/Microsoft.OperationsManagement/solutions/deploy.bicep b/modules/Microsoft.OperationsManagement/solutions/deploy.bicep new file mode 100644 index 0000000..ea7ee5d --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/deploy.bicep @@ -0,0 +1,63 @@ +@description('Required. Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`.') +param name string + +@description('Required. Name of the Log Analytics workspace where the solution will be deployed/enabled.') +param logAnalyticsWorkspaceName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive.') +param product string = 'OMSGallery' + +@description('Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`.') +param publisher string = 'Microsoft' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +var solutionName = publisher == 'Microsoft' ? '${name}(${logAnalyticsWorkspace.name})' : name + +var solutionProduct = publisher == 'Microsoft' ? 'OMSGallery/${name}' : product + +resource solution 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = { + name: solutionName + location: location + properties: { + workspaceResourceId: logAnalyticsWorkspace.id + } + plan: { + name: solutionName + promotionCode: '' + product: solutionProduct + publisher: publisher + } +} + +@description('The name of the deployed solution.') +output name string = solution.name + +@description('The resource ID of the deployed solution.') +output resourceId string = solution.id + +@description('The resource group where the solution is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = solution.location diff --git a/modules/Microsoft.OperationsManagement/solutions/readme.md b/modules/Microsoft.OperationsManagement/solutions/readme.md new file mode 100644 index 0000000..e53e89e --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/readme.md @@ -0,0 +1,203 @@ +# OperationsManagement Solutions `[Microsoft.OperationsManagement/solutions]` + +This module deploys OperationsManagement Solutions. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | Name of the Log Analytics workspace where the solution will be deployed/enabled. | +| `name` | string | Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `product` | string | `'OMSGallery'` | The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive. | +| `publisher` | string | `'Microsoft'` | The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed solution. | +| `resourceGroupName` | string | The resource group where the solution is deployed. | +| `resourceId` | string | The resource ID of the deployed solution. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Solutions' + params: { + // Required parameters + logAnalyticsWorkspaceName: 'adp-<>-az-law-sol-001' + name: 'Updates' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + }, + "name": { + "value": "Updates" + } + } +} +``` + +
+

+ +

Example 2: Ms

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Solutions' + params: { + // Required parameters + logAnalyticsWorkspaceName: 'adp-<>-az-law-sol-001' + name: 'AzureAutomation' + // Non-required parameters + product: 'OMSGallery' + publisher: 'Microsoft' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + }, + "name": { + "value": "AzureAutomation" + }, + // Non-required parameters + "product": { + "value": "OMSGallery" + }, + "publisher": { + "value": "Microsoft" + } + } +} +``` + +
+

+ +

Example 3: Nonms

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Solutions' + params: { + // Required parameters + logAnalyticsWorkspaceName: 'adp-<>-az-law-sol-001' + name: 'nonmsTestSolution' + // Non-required parameters + product: 'nonmsTestSolutionProduct' + publisher: 'nonmsTestSolutionPublisher' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "adp-<>-az-law-sol-001" + }, + "name": { + "value": "nonmsTestSolution" + }, + // Non-required parameters + "product": { + "value": "nonmsTestSolutionProduct" + }, + "publisher": { + "value": "nonmsTestSolutionPublisher" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.OperationsManagement/solutions/version.json b/modules/Microsoft.OperationsManagement/solutions/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.OperationsManagement/solutions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.PowerBIDedicated/capacities/.bicep/nested_rbac.bicep b/modules/Microsoft.PowerBIDedicated/capacities/.bicep/nested_rbac.bicep new file mode 100644 index 0000000..e2e10ca --- /dev/null +++ b/modules/Microsoft.PowerBIDedicated/capacities/.bicep/nested_rbac.bicep @@ -0,0 +1,31 @@ +param roleAssignmentObj object +param resourceName 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 powerbi 'Microsoft.PowerBIDedicated/capacities@2021-01-01' existing = { + name: resourceName +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in roleAssignmentObj.principalIds: { + name: guid(powerbi.name, principalId, roleAssignmentObj.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignmentObj.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignmentObj.roleDefinitionIdOrName] : roleAssignmentObj.roleDefinitionIdOrName + principalId: principalId + } + scope: powerbi +}] diff --git a/modules/Microsoft.PowerBIDedicated/capacities/.test/min.parameters.json b/modules/Microsoft.PowerBIDedicated/capacities/.test/min.parameters.json new file mode 100644 index 0000000..a7852b5 --- /dev/null +++ b/modules/Microsoft.PowerBIDedicated/capacities/.test/min.parameters.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azpbid001" + }, + "skuCapacity": { + "value": 1 + }, + "members": { + "value": [ + "<>" + ] + } + } +} diff --git a/modules/Microsoft.PowerBIDedicated/capacities/deploy.bicep b/modules/Microsoft.PowerBIDedicated/capacities/deploy.bicep new file mode 100644 index 0000000..72ee301 --- /dev/null +++ b/modules/Microsoft.PowerBIDedicated/capacities/deploy.bicep @@ -0,0 +1,98 @@ +@description('Required. Name of the PowerBI Embedded.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Required. SkuCapacity of the resource.') +param skuCapacity int + +@allowed([ + 'A1' + 'A2' + 'A3' + 'A4' + 'A5' + 'A6' +]) +@description('Optional. SkuCapacity of the resource.') +param skuName string = 'A1' + +@allowed([ + 'AutoPremiumHost' + 'PBIE_Azure' + 'Premium' +]) +@description('Optional. SkuCapacity of the resource.') +param skuTier string = 'PBIE_Azure' + +@description('Required. Members of the resource.') +param members array + +@allowed([ + 'Gen1' + 'Gen2' +]) +@description('Optional. Mode of the resource.') +param mode string = 'Gen2' + +@allowed([ + '' + 'CanNotDelete' + 'NotSpecified' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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 = [] + +resource powerbi 'Microsoft.PowerBIDedicated/capacities@2021-01-01' = { + name: name + location: location + tags: tags + sku: { + capacity: skuCapacity + name: skuName + tier: skuTier + } + properties: { + administration: { + members: members + } + mode: mode + } +} + +resource powerbi_lock 'Microsoft.Authorization/locks@2016-09-01' = if (!empty(lock)) { + name: '${powerbi.name}-${lock}-lock' + properties: { + level: lock + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: powerbi +} + +module powerbi_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-rbac-${index}' + params: { + roleAssignmentObj: roleAssignment + resourceName: powerbi.name + } +}] + +@description('The resource ID of the PowerBi Embedded.') +output resourceId string = powerbi.id + +@description('The name of the resource group the PowerBi Embedded was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The Name of the PowerBi Embedded.') +output name string = powerbi.name + +@description('The location the resource was deployed into.') +output location string = powerbi.location diff --git a/modules/Microsoft.PowerBIDedicated/capacities/readme.md b/modules/Microsoft.PowerBIDedicated/capacities/readme.md new file mode 100644 index 0000000..e5bb833 --- /dev/null +++ b/modules/Microsoft.PowerBIDedicated/capacities/readme.md @@ -0,0 +1,211 @@ +# PowerBIDedicated Capacities `[Microsoft.PowerBIDedicated/capacities]` + +This module deploys PowerBIDedicated Capacities. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2016-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2016-09-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.PowerBIDedicated/capacities` | [2021-01-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.PowerBIDedicated/2021-01-01/capacities) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `members` | array | Members of the resource. | +| `name` | string | Name of the PowerBI Embedded. | +| `skuCapacity` | int | SkuCapacity of the resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, NotSpecified, ReadOnly]` | Specify the type of lock. | +| `mode` | string | `'Gen2'` | `[Gen1, Gen2]` | Mode of the resource. | +| `roleAssignments` | array | `[]` | | 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'. | +| `skuName` | string | `'A1'` | `[A1, A2, A3, A4, A5, A6]` | SkuCapacity of the resource. | +| `skuTier` | string | `'PBIE_Azure'` | `[AutoPremiumHost, PBIE_Azure, Premium]` | SkuCapacity of the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the PowerBi Embedded. | +| `resourceGroupName` | string | The name of the resource group the PowerBi Embedded was created in. | +| `resourceId` | string | The resource ID of the PowerBi Embedded. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module capacities './Microsoft.PowerBIDedicated/capacities/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Capacities' + params: { + // Required parameters + members: [ + '<>' + ] + name: '<>azpbid001' + skuCapacity: 1 + } +} +``` + +
+

+ +

+ +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 + "members": { + "value": [ + "<>" + ] + }, + "name": { + "value": "<>azpbid001" + }, + "skuCapacity": { + "value": 1 + } + } +} +``` + +
+

diff --git a/modules/Microsoft.PowerBIDedicated/capacities/version.json b/modules/Microsoft.PowerBIDedicated/capacities/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.PowerBIDedicated/capacities/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.RecoveryServices/vaults/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..afff6bd --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,75 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'Backup Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a795c7a0-d4a2-40c1-ae25-d81f01202912') + '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') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Site Recovery Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dbaa88c4-0c30-4179-9fb3-46319faa6149') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource rsv 'Microsoft.RecoveryServices/vaults@2021-12-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(rsv.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: rsv +}] diff --git a/modules/Microsoft.RecoveryServices/vaults/.test/dr.parameters.json b/modules/Microsoft.RecoveryServices/vaults/.test/dr.parameters.json new file mode 100644 index 0000000..53bc617 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/.test/dr.parameters.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-rsv-dr-001" + }, + "replicationFabrics": { + "value": [ + { + "location": "NorthEurope", + "replicationContainers": [ + { + "name": "ne-container1", + "replicationContainerMappings": [ + { + "targetProtectionContainerId": "/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-min-001/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container2", + "policyName": "Default_values", + "targetContainerName": "pluto" + } + ] + }, + { + "name": "ne-container2", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerFabricName": "WE-2", + "targetContainerName": "we-container1" + } + ] + } + ] + }, + { + "name": "WE-2", + "location": "WestEurope", + "replicationContainers": [ + { + "name": "we-container1", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerFabricName": "NorthEurope", + "targetContainerName": "ne-container2" + } + ] + } + ] + } + ] + }, + "replicationPolicies": { + "value": [ + { + "name": "Default_values" + }, + { + "name": "Custom_values", + "appConsistentFrequencyInMinutes": 240, + "crashConsistentFrequencyInMinutes": 7, + "multiVmSyncStatus": "Disable", + "recoveryPointHistory": 2880 + } + ] + } + } +} diff --git a/modules/Microsoft.RecoveryServices/vaults/.test/min.parameters.json b/modules/Microsoft.RecoveryServices/vaults/.test/min.parameters.json new file mode 100644 index 0000000..81ba350 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/.test/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": "<>-az-rsv-min-001" + } + } +} diff --git a/modules/Microsoft.RecoveryServices/vaults/.test/parameters.json b/modules/Microsoft.RecoveryServices/vaults/.test/parameters.json new file mode 100644 index 0000000..060b2d2 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/.test/parameters.json @@ -0,0 +1,302 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-rsv-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "backupConfig": { + "value": { + "enhancedSecurityState": "Disabled", + "softDeleteFeatureState": "Disabled" + } + }, + "backupPolicies": { + "value": [ + { + "name": "VMpolicy", + "properties": { + "backupManagementType": "AzureIaasVM", + "instantRPDetails": {}, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T07:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 180, + "durationType": "Days" + } + }, + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 12, + "durationType": "Weeks" + } + }, + "monthlySchedule": { + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 60, + "durationType": "Months" + } + }, + "yearlySchedule": { + "retentionScheduleFormatType": "Weekly", + "monthsOfYear": [ + "January" + ], + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + } + } + }, + "instantRpRetentionRangeInDays": 2, + "timeZone": "UTC", + "protectedItemsCount": 0 + } + }, + { + "name": "sqlpolicy", + "properties": { + "backupManagementType": "AzureWorkload", + "workLoadType": "SQLDataBase", + "settings": { + "timeZone": "UTC", + "issqlcompression": true, + "isCompression": true + }, + "subProtectionPolicy": [ + { + "policyType": "Full", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Weekly", + "scheduleRunDays": [ + "Sunday" + ], + "scheduleRunTimes": [ + "2019-11-07T22:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 104, + "durationType": "Weeks" + } + }, + "monthlySchedule": { + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 60, + "durationType": "Months" + } + }, + "yearlySchedule": { + "retentionScheduleFormatType": "Weekly", + "monthsOfYear": [ + "January" + ], + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + } + } + } + }, + { + "policyType": "Differential", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Weekly", + "scheduleRunDays": [ + "Monday" + ], + "scheduleRunTimes": [ + "2017-03-07T02:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "SimpleRetentionPolicy", + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + { + "policyType": "Log", + "schedulePolicy": { + "schedulePolicyType": "LogSchedulePolicy", + "scheduleFrequencyInMins": 120 + }, + "retentionPolicy": { + "retentionPolicyType": "SimpleRetentionPolicy", + "retentionDuration": { + "count": 15, + "durationType": "Days" + } + } + } + ], + "protectedItemsCount": 0 + } + }, + { + "name": "filesharepolicy", + "properties": { + "backupManagementType": "AzureStorage", + "workloadType": "AzureFileShare", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T04:30:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "2019-11-07T04:30:00Z" + ], + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + "timeZone": "UTC", + "protectedItemsCount": 0 + } + } + ] + }, + "backupStorageConfig": { + "value": { + "storageModelType": "GeoRedundant", + "crossRegionRestoreFlag": true + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "AzureSiteRecovery", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.siterecovery.windowsazure.com" + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} diff --git a/modules/Microsoft.RecoveryServices/vaults/backupConfig/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/backupConfig/deploy.bicep new file mode 100644 index 0000000..9c4cee8 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupConfig/deploy.bicep @@ -0,0 +1,92 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Optional. Name of the Azure Recovery Service Vault Backup Policy.') +param name string = 'vaultconfig' + +@description('Optional. Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param enhancedSecurityState string = 'Enabled' + +@description('Optional. ResourceGuard Operation Requests.') +param resourceGuardOperationRequests array = [] + +@description('Optional. Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param softDeleteFeatureState string = 'Enabled' + +@description('Optional. Storage type.') +@allowed([ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' +]) +param storageModelType string = 'GeoRedundant' + +@description('Optional. Storage type.') +@allowed([ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' +]) +param storageType string = 'GeoRedundant' + +@description('Optional. Once a machine is registered against a resource, the storageTypeState is always Locked.') +@allowed([ + 'Locked' + 'Unlocked' +]) +param storageTypeState string = 'Locked' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Is soft delete feature state editable.') +param isSoftDeleteFeatureStateEditable bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource rsv 'Microsoft.RecoveryServices/vaults@2022-02-01' existing = { + name: recoveryVaultName +} + +resource backupConfig 'Microsoft.RecoveryServices/vaults/backupconfig@2022-02-01' = { + name: name + parent: rsv + properties: { + enhancedSecurityState: enhancedSecurityState + resourceGuardOperationRequests: resourceGuardOperationRequests + softDeleteFeatureState: softDeleteFeatureState + storageModelType: storageModelType + storageType: storageType + storageTypeState: storageTypeState + isSoftDeleteFeatureStateEditable: isSoftDeleteFeatureStateEditable + } +} + +@description('The name of the backup config.') +output name string = backupConfig.name + +@description('The resource ID of the backup config.') +output resourceId string = backupConfig.id + +@description('The name of the resource group the backup config was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/backupConfig/readme.md b/modules/Microsoft.RecoveryServices/vaults/backupConfig/readme.md new file mode 100644 index 0000000..5378956 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupConfig/readme.md @@ -0,0 +1,49 @@ +# Recovery Services Vault Backup Config `[Microsoft.RecoveryServices/vaults/backupconfig]` + +This module deploys recovery services vault backup config. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/backupconfig` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupconfig) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enhancedSecurityState` | string | `'Enabled'` | `[Disabled, Enabled]` | Enable this setting to protect hybrid backups against accidental deletes and add additional layer of authentication for critical operations. | +| `isSoftDeleteFeatureStateEditable` | bool | `True` | | Is soft delete feature state editable. | +| `name` | string | `'vaultconfig'` | | Name of the Azure Recovery Service Vault Backup Policy. | +| `resourceGuardOperationRequests` | array | `[]` | | ResourceGuard Operation Requests. | +| `softDeleteFeatureState` | string | `'Enabled'` | `[Disabled, Enabled]` | Enable this setting to protect backup data for Azure VM, SQL Server in Azure VM and SAP HANA in Azure VM from accidental deletes. | +| `storageModelType` | string | `'GeoRedundant'` | `[GeoRedundant, LocallyRedundant, ReadAccessGeoZoneRedundant, ZoneRedundant]` | Storage type. | +| `storageType` | string | `'GeoRedundant'` | `[GeoRedundant, LocallyRedundant, ReadAccessGeoZoneRedundant, ZoneRedundant]` | Storage type. | +| `storageTypeState` | string | `'Locked'` | `[Locked, Unlocked]` | Once a machine is registered against a resource, the storageTypeState is always Locked. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backup config. | +| `resourceGroupName` | string | The name of the resource group the backup config was created in. | +| `resourceId` | string | The resource ID of the backup config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/backupConfig/version.json b/modules/Microsoft.RecoveryServices/vaults/backupConfig/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupConfig/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/backupPolicies/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/deploy.bicep new file mode 100644 index 0000000..c6b28a9 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/deploy.bicep @@ -0,0 +1,42 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Required. Name of the Azure Recovery Service Vault Backup Policy.') +param name string + +@description('Required. Configuration of the Azure Recovery Service Vault Backup Policy.') +param backupPolicyProperties object + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource rsv 'Microsoft.RecoveryServices/vaults@2022-02-01' existing = { + name: recoveryVaultName +} + +resource backupPolicy 'Microsoft.RecoveryServices/vaults/backupPolicies@2022-02-01' = { + name: name + parent: rsv + properties: backupPolicyProperties +} + +@description('The name of the backup policy.') +output name string = backupPolicy.name + +@description('The resource ID of the backup policy.') +output resourceId string = backupPolicy.id + +@description('The name of the resource group the backup policy was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/backupPolicies/readme.md b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/readme.md new file mode 100644 index 0000000..62e3046 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/readme.md @@ -0,0 +1,229 @@ +# RecoveryServicesVaultsBackupPolicies `[Microsoft.RecoveryServices/vaults/backupPolicies]` + +This module deploys a Backup Policy for a Recovery Services Vault + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `backupPolicyProperties` | object | Configuration of the Azure Recovery Service Vault Backup Policy. | +| `name` | string | Name of the Azure Recovery Service Vault Backup Policy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +### Parameter Usage: `backupPolicyProperties` + +Object continaining the configuration for backup policies. It needs to be properly formatted and can be VM backup policies, SQL on VM backup policies or fileshare policies. The following example shows a VM backup policy. + +

+ +Parameter JSON format + +```json +"backupPolicyProperties": { + "value": { + "backupManagementType": "AzureIaasVM", + "instantRPDetails": {}, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T07:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 180, + "durationType": "Days" + } + }, + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 12, + "durationType": "Weeks" + } + }, + "monthlySchedule": { + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 60, + "durationType": "Months" + } + }, + "yearlySchedule": { + "retentionScheduleFormatType": "Weekly", + "monthsOfYear": [ + "January" + ], + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + } + } + }, + "instantRpRetentionRangeInDays": 2, + "timeZone": "UTC", + "protectedItemsCount": 0 + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +backupPolicyProperties: { + backupManagementType: 'AzureIaasVM' + instantRPDetails: {} + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Daily' + scheduleRunTimes: [ + '2019-11-07T07:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + retentionPolicy: { + retentionPolicyType: 'LongTermRetentionPolicy' + dailySchedule: { + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 180 + durationType: 'Days' + } + } + weeklySchedule: { + daysOfTheWeek: [ + 'Sunday' + ] + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 12 + durationType: 'Weeks' + } + } + monthlySchedule: { + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 60 + durationType: 'Months' + } + } + yearlySchedule: { + retentionScheduleFormatType: 'Weekly' + monthsOfYear: [ + 'January' + ] + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 10 + durationType: 'Years' + } + } + } + instantRpRetentionRangeInDays: 2 + timeZone: 'UTC' + protectedItemsCount: 0 +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backup policy. | +| `resourceGroupName` | string | The name of the resource group the backup policy was created in. | +| `resourceId` | string | The resource ID of the backup policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/backupPolicies/version.json b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/deploy.bicep new file mode 100644 index 0000000..b99c0e6 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/deploy.bicep @@ -0,0 +1,54 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Optional. The name of the backup storage config.') +param name string = 'vaultstorageconfig' + +@description('Optional. Change Vault Storage Type (Works if vault has not registered any backup instance).') +@allowed([ + 'GeoRedundant' + 'LocallyRedundant' + 'ReadAccessGeoZoneRedundant' + 'ZoneRedundant' +]) +param storageModelType string = 'GeoRedundant' + +@description('Optional. Opt in details of Cross Region Restore feature.') +param crossRegionRestoreFlag bool = true + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource rsv 'Microsoft.RecoveryServices/vaults@2022-02-01' existing = { + name: recoveryVaultName +} + +resource backupStorageConfig 'Microsoft.RecoveryServices/vaults/backupstorageconfig@2022-02-01' = { + name: name + parent: rsv + properties: { + storageModelType: storageModelType + crossRegionRestoreFlag: crossRegionRestoreFlag + } +} + +@description('The name of the backup storage config.') +output name string = backupStorageConfig.name + +@description('The resource ID of the backup storage config.') +output resourceId string = backupStorageConfig.id + +@description('The name of the Resource Group the backup storage configuration was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/readme.md b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/readme.md new file mode 100644 index 0000000..c100885 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/readme.md @@ -0,0 +1,43 @@ +# RecoveryServicesVaultsBackupStorageConfig `[Microsoft.RecoveryServices/vaults/backupstorageconfig]` + +This module deploys the Backup Storage Configuration for the Recovery Service Vault +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/backupstorageconfig` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupstorageconfig) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `crossRegionRestoreFlag` | bool | `True` | | Opt in details of Cross Region Restore feature. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'vaultstorageconfig'` | | The name of the backup storage config. | +| `storageModelType` | string | `'GeoRedundant'` | `[GeoRedundant, LocallyRedundant, ReadAccessGeoZoneRedundant, ZoneRedundant]` | Change Vault Storage Type (Works if vault has not registered any backup instance). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backup storage config. | +| `resourceGroupName` | string | The name of the Resource Group the backup storage configuration was created in. | +| `resourceId` | string | The resource ID of the backup storage config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/version.json b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/backupStorageConfig/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/deploy.bicep new file mode 100644 index 0000000..0791822 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/deploy.bicep @@ -0,0 +1,314 @@ +@description('Required. Name of the Azure Recovery Service Vault.') +param name string + +@description('Optional. The storage configuration for the Azure Recovery Service Vault.') +param backupStorageConfig object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. List of all backup policies.') +param backupPolicies array = [] + +@description('Optional. The backup configuration.') +param backupConfig object = {} + +@description('Optional. List of all protection containers.') +@minLength(0) +param protectionContainers array = [] + +@description('Optional. List of all replication fabrics.') +@minLength(0) +param replicationFabrics array = [] + +@description('Optional. List of all replication policies.') +@minLength(0) +param replicationPolicies array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Tags of the Recovery Service Vault resource.') +param tags object = {} + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'AzureBackupReport' + 'CoreAzureBackup' + 'AddonAzureBackupJobs' + 'AddonAzureBackupAlerts' + 'AddonAzureBackupPolicy' + 'AddonAzureBackupStorage' + 'AddonAzureBackupProtectedInstance' + 'AzureSiteRecoveryJobs' + 'AzureSiteRecoveryEvents' + 'AzureSiteRecoveryReplicatedItems' + 'AzureSiteRecoveryReplicationStats' + 'AzureSiteRecoveryRecoveryPoints' + 'AzureSiteRecoveryReplicationDataUploadRate' + 'AzureSiteRecoveryProtectedDiskDataChurn' +]) +param diagnosticLogCategoriesToEnable array = [ + 'AzureBackupReport' + 'CoreAzureBackup' + 'AddonAzureBackupJobs' + 'AddonAzureBackupAlerts' + 'AddonAzureBackupPolicy' + 'AddonAzureBackupStorage' + 'AddonAzureBackupProtectedInstance' + 'AzureSiteRecoveryJobs' + 'AzureSiteRecoveryEvents' + 'AzureSiteRecoveryReplicatedItems' + 'AzureSiteRecoveryReplicationStats' + 'AzureSiteRecoveryRecoveryPoints' + 'AzureSiteRecoveryReplicationDataUploadRate' + 'AzureSiteRecoveryProtectedDiskDataChurn' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Health' +]) +param diagnosticMetricsToEnable array = [ + 'Health' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource rsv 'Microsoft.RecoveryServices/vaults@2022-02-01' = { + name: name + location: location + tags: tags + identity: any(identity) + sku: { + name: 'RS0' + tier: 'Standard' + } + properties: {} +} + +module rsv_replicationFabrics 'replicationFabrics/deploy.bicep' = [for (replicationFabric, index) in replicationFabrics: { + name: '${uniqueString(deployment().name, location)}-RSV-Fabric-${index}' + params: { + recoveryVaultName: rsv.name + name: contains(replicationFabric, 'name') ? replicationFabric.name : replicationFabric.location + location: replicationFabric.location + replicationContainers: contains(replicationFabric, 'replicationContainers') ? replicationFabric.replicationContainers : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + rsv_replicationPolicies + ] +}] + +module rsv_replicationPolicies 'replicationPolicies/deploy.bicep' = [for (replicationPolicy, index) in replicationPolicies: { + name: '${uniqueString(deployment().name, location)}-RSV-Policy-${index}' + params: { + name: replicationPolicy.name + recoveryVaultName: rsv.name + appConsistentFrequencyInMinutes: contains(replicationPolicy, 'appConsistentFrequencyInMinutes') ? replicationPolicy.appConsistentFrequencyInMinutes : 60 + crashConsistentFrequencyInMinutes: contains(replicationPolicy, 'crashConsistentFrequencyInMinutes') ? replicationPolicy.crashConsistentFrequencyInMinutes : 5 + multiVmSyncStatus: contains(replicationPolicy, 'multiVmSyncStatus') ? replicationPolicy.multiVmSyncStatus : 'Enable' + recoveryPointHistory: contains(replicationPolicy, 'recoveryPointHistory') ? replicationPolicy.recoveryPointHistory : 1440 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module rsv_backupStorageConfiguration 'backupStorageConfig/deploy.bicep' = if (!empty(backupStorageConfig)) { + name: '${uniqueString(deployment().name, location)}-RSV-BackupStorageConfig' + params: { + recoveryVaultName: rsv.name + storageModelType: backupStorageConfig.storageModelType + crossRegionRestoreFlag: backupStorageConfig.crossRegionRestoreFlag + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module rsv_protectionContainers 'protectionContainers/deploy.bicep' = [for (protectionContainer, index) in protectionContainers: { + name: '${uniqueString(deployment().name, location)}-RSV-ProtectionContainers-${index}' + params: { + recoveryVaultName: rsv.name + name: protectionContainer.name + sourceResourceId: protectionContainer.sourceResourceId + friendlyName: protectionContainer.friendlyName + backupManagementType: protectionContainer.backupManagementType + containerType: protectionContainer.containerType + enableDefaultTelemetry: enableReferencedModulesTelemetry + protectedItems: contains(protectionContainer, 'protectedItems') ? protectionContainer.protectedItems : [] + location: location + } +}] + +module rsv_backupPolicies 'backupPolicies/deploy.bicep' = [for (backupPolicy, index) in backupPolicies: { + name: '${uniqueString(deployment().name, location)}-RSV-BackupPolicy-${index}' + params: { + recoveryVaultName: rsv.name + name: backupPolicy.name + backupPolicyProperties: backupPolicy.properties + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module rsv_backupConfig 'backupConfig/deploy.bicep' = if (!empty(backupConfig)) { + name: '${uniqueString(deployment().name, location)}-RSV-BackupConfig' + params: { + recoveryVaultName: rsv.name + name: contains(backupConfig, 'name') ? backupConfig.name : 'vaultconfig' + enhancedSecurityState: contains(backupConfig, 'enhancedSecurityState') ? backupConfig.enhancedSecurityState : 'Enabled' + resourceGuardOperationRequests: contains(backupConfig, 'resourceGuardOperationRequests') ? backupConfig.resourceGuardOperationRequests : [] + softDeleteFeatureState: contains(backupConfig, 'softDeleteFeatureState') ? backupConfig.softDeleteFeatureState : 'Enabled' + storageModelType: contains(backupConfig, 'storageModelType') ? backupConfig.storageModelType : 'GeoRedundant' + storageType: contains(backupConfig, 'storageType') ? backupConfig.storageType : 'GeoRedundant' + storageTypeState: contains(backupConfig, 'storageTypeState') ? backupConfig.storageTypeState : 'Locked' + isSoftDeleteFeatureStateEditable: contains(backupConfig, 'isSoftDeleteFeatureStateEditable') ? backupConfig.isSoftDeleteFeatureStateEditable : true + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource rsv_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${rsv.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: rsv +} + +resource rsv_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: rsv +} + +module rsv_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-RSV-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(rsv.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: rsv.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module rsv_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-RSV-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: rsv.id + } +}] + +@description('The resource ID of the recovery services vault.') +output resourceId string = rsv.id + +@description('The name of the resource group the recovery services vault was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The Name of the recovery services vault.') +output name string = rsv.name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(rsv.identity, 'principalId') ? rsv.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = rsv.location diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/deploy.bicep new file mode 100644 index 0000000..68d3038 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/deploy.bicep @@ -0,0 +1,98 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Required. Name of the Azure Recovery Service Vault Protection Container.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Backup management type to execute the current Protection Container job.') +@allowed([ + 'AzureBackupServer' + 'AzureIaasVM' + 'AzureSql' + 'AzureStorage' + 'AzureWorkload' + 'DPM' + 'DefaultBackup' + 'Invalid' + 'MAB' + '' +]) +param backupManagementType string = '' + +@description('Optional. Resource ID of the target resource for the Protection Container.') +param sourceResourceId string = '' + +@description('Optional. Friendly name of the Protection Container.') +param friendlyName string = '' + +@description('Optional. Protected items to register in the container.') +param protectedItems array = [] + +@description('Optional. Type of the container.') +@allowed([ + 'AzureBackupServerContainer' + 'AzureSqlContainer' + 'GenericContainer' + 'Microsoft.ClassicCompute/virtualMachines' + 'Microsoft.Compute/virtualMachines' + 'SQLAGWorkLoadContainer' + 'StorageContainer' + 'VMAppContainer' + 'Windows' + '' +]) +param containerType string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource protectionContainer 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers@2022-02-01' = { + name: '${recoveryVaultName}/Azure/${name}' + properties: { + sourceResourceId: !empty(sourceResourceId) ? sourceResourceId : null + friendlyName: !empty(friendlyName) ? friendlyName : null + backupManagementType: !empty(backupManagementType) ? backupManagementType : null + containerType: !empty(containerType) ? any(containerType) : null + } +} + +module protectionContainer_protectedItems 'protectedItems/deploy.bicep' = [for (protectedItem, index) in protectedItems: { + name: '${uniqueString(deployment().name, location)}-ProtectedItem-${index}' + params: { + policyId: protectedItem.policyId + name: protectedItem.name + protectedItemType: protectedItem.protectedItemType + protectionContainerName: name + recoveryVaultName: recoveryVaultName + sourceResourceId: protectedItem.sourceResourceId + location: location + enableDefaultTelemetry: enableDefaultTelemetry + } + dependsOn: [ + protectionContainer + ] +}] + +@description('The name of the Resource Group the Protection Container was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Protection Container.') +output resourceId string = protectionContainer.id + +@description('The Name of the Protection Container.') +output name string = protectionContainer.name diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/deploy.bicep new file mode 100644 index 0000000..b48824b --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/deploy.bicep @@ -0,0 +1,66 @@ +@description('Required. Name of the resource.') +param name string + +@description('Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment.') +param protectionContainerName string + +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + 'AzureFileShareProtectedItem' + 'AzureVmWorkloadSAPAseDatabase' + 'AzureVmWorkloadSAPHanaDatabase' + 'AzureVmWorkloadSQLDatabase' + 'DPMProtectedItem' + 'GenericProtectedItem' + 'MabFileFolderProtectedItem' + 'Microsoft.ClassicCompute/virtualMachines' + 'Microsoft.Compute/virtualMachines' + 'Microsoft.Sql/servers/databases' +]) +@description('Required. The backup item type.') +param protectedItemType string + +@description('Required. ID of the backup policy with which this item is backed up.') +param policyId string + +@description('Required. Resource ID of the resource to back up.') +param sourceResourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2022-02-01' = { + name: '${recoveryVaultName}/Azure/${protectionContainerName}/${name}' + location: location + properties: { + protectedItemType: any(protectedItemType) + policyId: policyId + sourceResourceId: sourceResourceId + } +} + +@description('The name of the Resource Group the protected item was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the protected item.') +output resourceId string = protectedItem.id + +@description('The Name of the protected item.') +output name string = protectedItem.name diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/readme.md b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/readme.md new file mode 100644 index 0000000..1379744 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/readme.md @@ -0,0 +1,51 @@ +# Recovery Service Vault Protection Container Protected Item `[Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems]` + +This module deploys a Protection Container Protected Item for a Recovery Services Vault + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers/protectedItems) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Name of the resource. | +| `policyId` | string | | ID of the backup policy with which this item is backed up. | +| `protectedItemType` | string | `[AzureFileShareProtectedItem, AzureVmWorkloadSAPAseDatabase, AzureVmWorkloadSAPHanaDatabase, AzureVmWorkloadSQLDatabase, DPMProtectedItem, GenericProtectedItem, MabFileFolderProtectedItem, Microsoft.ClassicCompute/virtualMachines, Microsoft.Compute/virtualMachines, Microsoft.Sql/servers/databases]` | The backup item type. | +| `sourceResourceId` | string | | Resource ID of the resource to back up. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `protectionContainerName` | string | Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment. | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The Name of the protected item. | +| `resourceGroupName` | string | The name of the Resource Group the protected item was created in. | +| `resourceId` | string | The resource ID of the protected item. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/version.json b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/protectedItems/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/readme.md b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/readme.md new file mode 100644 index 0000000..3cfdef2 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/readme.md @@ -0,0 +1,53 @@ +# RecoveryServicesProtectionContainer `[Microsoft.RecoveryServices/vaults/protectionContainers]` + +This module deploys a Protection Container for a Recovery Services Vault + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers/protectedItems) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Recovery Service Vault Protection Container. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backupManagementType` | string | `''` | `['', AzureBackupServer, AzureIaasVM, AzureSql, AzureStorage, AzureWorkload, DefaultBackup, DPM, Invalid, MAB]` | Backup management type to execute the current Protection Container job. | +| `containerType` | string | `''` | `['', AzureBackupServerContainer, AzureSqlContainer, GenericContainer, Microsoft.ClassicCompute/virtualMachines, Microsoft.Compute/virtualMachines, SQLAGWorkLoadContainer, StorageContainer, VMAppContainer, Windows]` | Type of the container. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `friendlyName` | string | `''` | | Friendly name of the Protection Container. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `protectedItems` | _[protectedItems](protectedItems/readme.md)_ array | `[]` | | Protected items to register in the container. | +| `sourceResourceId` | string | `''` | | Resource ID of the target resource for the Protection Container. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The Name of the Protection Container. | +| `resourceGroupName` | string | The name of the Resource Group the Protection Container was created in. | +| `resourceId` | string | The resource ID of the Protection Container. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/protectionContainers/version.json b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/protectionContainers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/readme.md b/modules/Microsoft.RecoveryServices/vaults/readme.md new file mode 100644 index 0000000..d176ab6 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/readme.md @@ -0,0 +1,1705 @@ +# Recovery Services Vaults `[Microsoft.RecoveryServices/vaults]` + +This module deploys a recovery service vault. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.RecoveryServices/vaults` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults) | +| `Microsoft.RecoveryServices/vaults/backupconfig` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupconfig) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupFabrics/protectionContainers/protectedItems) | +| `Microsoft.RecoveryServices/vaults/backupPolicies` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupPolicies) | +| `Microsoft.RecoveryServices/vaults/backupstorageconfig` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/backupstorageconfig) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings) | +| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Recovery Service Vault. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backupConfig` | _[backupConfig](backupConfig/readme.md)_ object | `{object}` | | The backup configuration. | +| `backupPolicies` | _[backupPolicies](backupPolicies/readme.md)_ array | `[]` | | List of all backup policies. | +| `backupStorageConfig` | _[backupStorageConfig](backupStorageConfig/readme.md)_ object | `{object}` | | The storage configuration for the Azure Recovery Service Vault. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AddonAzureBackupAlerts, AddonAzureBackupJobs, AddonAzureBackupPolicy, AddonAzureBackupProtectedInstance, AddonAzureBackupStorage, AzureBackupReport, AzureSiteRecoveryEvents, AzureSiteRecoveryJobs, AzureSiteRecoveryProtectedDiskDataChurn, AzureSiteRecoveryRecoveryPoints, AzureSiteRecoveryReplicatedItems, AzureSiteRecoveryReplicationDataUploadRate, AzureSiteRecoveryReplicationStats, CoreAzureBackup]` | `[AddonAzureBackupAlerts, AddonAzureBackupJobs, AddonAzureBackupPolicy, AddonAzureBackupProtectedInstance, AddonAzureBackupStorage, AzureBackupReport, AzureSiteRecoveryEvents, AzureSiteRecoveryJobs, AzureSiteRecoveryProtectedDiskDataChurn, AzureSiteRecoveryRecoveryPoints, AzureSiteRecoveryReplicatedItems, AzureSiteRecoveryReplicationDataUploadRate, AzureSiteRecoveryReplicationStats, CoreAzureBackup]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Health]` | `[Health]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `protectionContainers` | _[protectionContainers](protectionContainers/readme.md)_ array | `[]` | | List of all protection containers. | +| `replicationFabrics` | _[replicationFabrics](replicationFabrics/readme.md)_ array | `[]` | | List of all replication fabrics. | +| `replicationPolicies` | _[replicationPolicies](replicationPolicies/readme.md)_ array | `[]` | | List of all replication policies. | +| `roleAssignments` | array | `[]` | | 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'. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Recovery Service Vault resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `backupStorageConfig` + +

+ +Parameter JSON format + +```json +"backupStorageConfig": { + "value": { + "storageModelType": "GeoRedundant", + "crossRegionRestoreFlag": true + } +} +``` + +
+ +
+ +Bicep format + +```bicep +backupStorageConfig: { + value: { + storageModelType: 'GeoRedundant' + crossRegionRestoreFlag: true + } +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `backupPolicies` + +Array of backup policies. They need to be properly formatted and can be VM backup policies, SQL on VM backup policies or fileshare policies. The following example shows all three types of backup policies. + +

+ +Parameter JSON format + +```json +"backupPolicies": { + "value": [ + { + "name": "VMpolicy", + "type": "Microsoft.RecoveryServices/vaults/backupPolicies", + "properties": { + "backupManagementType": "AzureIaasVM", + "instantRPDetails": {}, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T07:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 180, + "durationType": "Days" + } + }, + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 12, + "durationType": "Weeks" + } + }, + "monthlySchedule": { + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 60, + "durationType": "Months" + } + }, + "yearlySchedule": { + "retentionScheduleFormatType": "Weekly", + "monthsOfYear": [ + "January" + ], + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + } + } + }, + "instantRpRetentionRangeInDays": 2, + "timeZone": "UTC", + "protectedItemsCount": 0 + } + }, + { + "name": "sqlpolicy", + "type": "Microsoft.RecoveryServices/vaults/backupPolicies", + "properties": { + "backupManagementType": "AzureWorkload", + "workLoadType": "SQLDataBase", + "settings": { + "timeZone": "UTC", + "issqlcompression": true, + "isCompression": true + }, + "subProtectionPolicy": [ + { + "policyType": "Full", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Weekly", + "scheduleRunDays": [ + "Sunday" + ], + "scheduleRunTimes": [ + "2019-11-07T22:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 104, + "durationType": "Weeks" + } + }, + "monthlySchedule": { + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 60, + "durationType": "Months" + } + }, + "yearlySchedule": { + "retentionScheduleFormatType": "Weekly", + "monthsOfYear": [ + "January" + ], + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + } + } + } + }, + { + "policyType": "Differential", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Weekly", + "scheduleRunDays": [ + "Monday" + ], + "scheduleRunTimes": [ + "2017-03-07T02:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "SimpleRetentionPolicy", + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + { + "policyType": "Log", + "schedulePolicy": { + "schedulePolicyType": "LogSchedulePolicy", + "scheduleFrequencyInMins": 120 + }, + "retentionPolicy": { + "retentionPolicyType": "SimpleRetentionPolicy", + "retentionDuration": { + "count": 15, + "durationType": "Days" + } + } + } + ], + "protectedItemsCount": 0 + } + }, + { + "name": "filesharepolicy", + "type": "Microsoft.RecoveryServices/vaults/backupPolicies", + "properties": { + "backupManagementType": "AzureStorage", + "workloadType": "AzureFileShare", + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T04:30:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "2019-11-07T04:30:00Z" + ], + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + "timeZone": "UTC", + "protectedItemsCount": 0 + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +backupPolicies: [ + { + name: 'VMpolicy' + type: 'Microsoft.RecoveryServices/vaults/backupPolicies' + properties: { + backupManagementType: 'AzureIaasVM' + instantRPDetails: {} + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Daily' + scheduleRunTimes: [ + '2019-11-07T07:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + retentionPolicy: { + retentionPolicyType: 'LongTermRetentionPolicy' + dailySchedule: { + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 180 + durationType: 'Days' + } + } + weeklySchedule: { + daysOfTheWeek: [ + 'Sunday' + ] + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 12 + durationType: 'Weeks' + } + } + monthlySchedule: { + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 60 + durationType: 'Months' + } + } + yearlySchedule: { + retentionScheduleFormatType: 'Weekly' + monthsOfYear: [ + 'January' + ] + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + retentionDuration: { + count: 10 + durationType: 'Years' + } + } + } + instantRpRetentionRangeInDays: 2 + timeZone: 'UTC' + protectedItemsCount: 0 + } + } + { + name: 'sqlpolicy' + type: 'Microsoft.RecoveryServices/vaults/backupPolicies' + properties: { + backupManagementType: 'AzureWorkload' + workLoadType: 'SQLDataBase' + settings: { + timeZone: 'UTC' + issqlcompression: true + isCompression: true + } + subProtectionPolicy: [ + { + policyType: 'Full' + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Weekly' + scheduleRunDays: [ + 'Sunday' + ] + scheduleRunTimes: [ + '2019-11-07T22:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + retentionPolicy: { + retentionPolicyType: 'LongTermRetentionPolicy' + weeklySchedule: { + daysOfTheWeek: [ + 'Sunday' + ] + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + retentionDuration: { + count: 104 + durationType: 'Weeks' + } + } + monthlySchedule: { + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + retentionDuration: { + count: 60 + durationType: 'Months' + } + } + yearlySchedule: { + retentionScheduleFormatType: 'Weekly' + monthsOfYear: [ + 'January' + ] + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + retentionDuration: { + count: 10 + durationType: 'Years' + } + } + } + } + { + policyType: 'Differential' + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Weekly' + scheduleRunDays: [ + 'Monday' + ] + scheduleRunTimes: [ + '2017-03-07T02:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + retentionPolicy: { + retentionPolicyType: 'SimpleRetentionPolicy' + retentionDuration: { + count: 30 + durationType: 'Days' + } + } + } + { + policyType: 'Log' + schedulePolicy: { + schedulePolicyType: 'LogSchedulePolicy' + scheduleFrequencyInMins: 120 + } + retentionPolicy: { + retentionPolicyType: 'SimpleRetentionPolicy' + retentionDuration: { + count: 15 + durationType: 'Days' + } + } + } + ] + protectedItemsCount: 0 + } + } + { + name: 'filesharepolicy' + type: 'Microsoft.RecoveryServices/vaults/backupPolicies' + properties: { + backupManagementType: 'AzureStorage' + workloadType: 'AzureFileShare' + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Daily' + scheduleRunTimes: [ + '2019-11-07T04:30:00Z' + ] + scheduleWeeklyFrequency: 0 + } + retentionPolicy: { + retentionPolicyType: 'LongTermRetentionPolicy' + dailySchedule: { + retentionTimes: [ + '2019-11-07T04:30:00Z' + ] + retentionDuration: { + count: 30 + durationType: 'Days' + } + } + } + timeZone: 'UTC' + protectedItemsCount: 0 + } + } +] +``` + +
+

+ +### Parameter Usage: `replicationFabrics` + +

+ +Parameter JSON format + +```json +"replicationFabrics": { + "value": [ + { + "location": "NorthEurope", + "replicationContainers": [ + { + "name": "ne-container1", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerFabricName": "WestEurope-Fabric", + "targetContainerName": "we-conainer2" + } + ] + } + ] + }, + { + "name": "WestEurope-Fabric", //Optional + "location": "WestEurope", + "replicationContainers": [ + { + "name": "we-conainer2" + } + ] + } + ] +}, +``` + +### Parameter Usage: `replicationPolicies` + +
+ +Parameter JSON format + +```json +"replicationPolicies": { + "value": [ + { + "name": "Default_values" + }, + { + "name": "Custom_values", + "appConsistentFrequencyInMinutes": 240, + "crashConsistentFrequencyInMinutes": 7, + "multiVmSyncStatus": "Disable", + "recoveryPointHistory": 2880 + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +replicationPolicies: [ + { + name: 'Default_values' + } + { + name: 'Custom_values' + appConsistentFrequencyInMinutes: 240 + crashConsistentFrequencyInMinutes: 7 + multiVmSyncStatus: 'Disable' + recoveryPointHistory: 2880 + } +] +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the recovery services vault. | +| `resourceGroupName` | string | The name of the resource group the recovery services vault was created in. | +| `resourceId` | string | The resource ID of the recovery services vault. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Dr

+ +
+ +via Bicep module + +```bicep +module vaults './Microsoft.RecoveryServices/vaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Vaults' + params: { + // Required parameters + name: '<>-az-rsv-dr-001' + // Non-required parameters + replicationFabrics: [ + { + location: 'NorthEurope' + replicationContainers: [ + { + name: 'ne-container1' + replicationContainerMappings: [ + { + policyName: 'Default_values' + targetContainerName: 'pluto' + targetProtectionContainerId: '/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-min-001/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container2' + } + ] + } + { + name: 'ne-container2' + replicationContainerMappings: [ + { + policyName: 'Default_values' + targetContainerFabricName: 'WE-2' + targetContainerName: 'we-container1' + } + ] + } + ] + } + { + location: 'WestEurope' + name: 'WE-2' + replicationContainers: [ + { + name: 'we-container1' + replicationContainerMappings: [ + { + policyName: 'Default_values' + targetContainerFabricName: 'NorthEurope' + targetContainerName: 'ne-container2' + } + ] + } + ] + } + ] + replicationPolicies: [ + { + name: 'Default_values' + } + { + appConsistentFrequencyInMinutes: 240 + crashConsistentFrequencyInMinutes: 7 + multiVmSyncStatus: 'Disable' + name: 'Custom_values' + recoveryPointHistory: 2880 + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-rsv-dr-001" + }, + // Non-required parameters + "replicationFabrics": { + "value": [ + { + "location": "NorthEurope", + "replicationContainers": [ + { + "name": "ne-container1", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerName": "pluto", + "targetProtectionContainerId": "/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-min-001/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container2" + } + ] + }, + { + "name": "ne-container2", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerFabricName": "WE-2", + "targetContainerName": "we-container1" + } + ] + } + ] + }, + { + "location": "WestEurope", + "name": "WE-2", + "replicationContainers": [ + { + "name": "we-container1", + "replicationContainerMappings": [ + { + "policyName": "Default_values", + "targetContainerFabricName": "NorthEurope", + "targetContainerName": "ne-container2" + } + ] + } + ] + } + ] + }, + "replicationPolicies": { + "value": [ + { + "name": "Default_values" + }, + { + "appConsistentFrequencyInMinutes": 240, + "crashConsistentFrequencyInMinutes": 7, + "multiVmSyncStatus": "Disable", + "name": "Custom_values", + "recoveryPointHistory": 2880 + } + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module vaults './Microsoft.RecoveryServices/vaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Vaults' + params: { + name: '<>-az-rsv-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-rsv-min-001" + } + } +} +``` + +
+

+ +

Example 3: Parameters

+ +
+ +via Bicep module + +```bicep +module vaults './Microsoft.RecoveryServices/vaults/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Vaults' + params: { + // Required parameters + name: '<>-az-rsv-x-001' + // Non-required parameters + backupConfig: { + enhancedSecurityState: 'Disabled' + softDeleteFeatureState: 'Disabled' + } + backupPolicies: [ + { + name: 'VMpolicy' + properties: { + backupManagementType: 'AzureIaasVM' + instantRPDetails: {} + instantRpRetentionRangeInDays: 2 + protectedItemsCount: 0 + retentionPolicy: { + dailySchedule: { + retentionDuration: { + count: 180 + durationType: 'Days' + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + } + monthlySchedule: { + retentionDuration: { + count: 60 + durationType: 'Months' + } + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + } + retentionPolicyType: 'LongTermRetentionPolicy' + weeklySchedule: { + daysOfTheWeek: [ + 'Sunday' + ] + retentionDuration: { + count: 12 + durationType: 'Weeks' + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + } + yearlySchedule: { + monthsOfYear: [ + 'January' + ] + retentionDuration: { + count: 10 + durationType: 'Years' + } + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T07:00:00Z' + ] + } + } + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Daily' + scheduleRunTimes: [ + '2019-11-07T07:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + timeZone: 'UTC' + } + } + { + name: 'sqlpolicy' + properties: { + backupManagementType: 'AzureWorkload' + protectedItemsCount: 0 + settings: { + isCompression: true + issqlcompression: true + timeZone: 'UTC' + } + subProtectionPolicy: [ + { + policyType: 'Full' + retentionPolicy: { + monthlySchedule: { + retentionDuration: { + count: 60 + durationType: 'Months' + } + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + } + retentionPolicyType: 'LongTermRetentionPolicy' + weeklySchedule: { + daysOfTheWeek: [ + 'Sunday' + ] + retentionDuration: { + count: 104 + durationType: 'Weeks' + } + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + } + yearlySchedule: { + monthsOfYear: [ + 'January' + ] + retentionDuration: { + count: 10 + durationType: 'Years' + } + retentionScheduleFormatType: 'Weekly' + retentionScheduleWeekly: { + daysOfTheWeek: [ + 'Sunday' + ] + weeksOfTheMonth: [ + 'First' + ] + } + retentionTimes: [ + '2019-11-07T22:00:00Z' + ] + } + } + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunDays: [ + 'Sunday' + ] + scheduleRunFrequency: 'Weekly' + scheduleRunTimes: [ + '2019-11-07T22:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + } + { + policyType: 'Differential' + retentionPolicy: { + retentionDuration: { + count: 30 + durationType: 'Days' + } + retentionPolicyType: 'SimpleRetentionPolicy' + } + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunDays: [ + 'Monday' + ] + scheduleRunFrequency: 'Weekly' + scheduleRunTimes: [ + '2017-03-07T02:00:00Z' + ] + scheduleWeeklyFrequency: 0 + } + } + { + policyType: 'Log' + retentionPolicy: { + retentionDuration: { + count: 15 + durationType: 'Days' + } + retentionPolicyType: 'SimpleRetentionPolicy' + } + schedulePolicy: { + scheduleFrequencyInMins: 120 + schedulePolicyType: 'LogSchedulePolicy' + } + } + ] + workLoadType: 'SQLDataBase' + } + } + { + name: 'filesharepolicy' + properties: { + backupManagementType: 'AzureStorage' + protectedItemsCount: 0 + retentionPolicy: { + dailySchedule: { + retentionDuration: { + count: 30 + durationType: 'Days' + } + retentionTimes: [ + '2019-11-07T04:30:00Z' + ] + } + retentionPolicyType: 'LongTermRetentionPolicy' + } + schedulePolicy: { + schedulePolicyType: 'SimpleSchedulePolicy' + scheduleRunFrequency: 'Daily' + scheduleRunTimes: [ + '2019-11-07T04:30:00Z' + ] + scheduleWeeklyFrequency: 0 + } + timeZone: 'UTC' + workloadType: 'AzureFileShare' + } + } + ] + backupStorageConfig: { + crossRegionRestoreFlag: true + storageModelType: 'GeoRedundant' + } + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.siterecovery.windowsazure.com' + ] + } + service: 'AzureSiteRecovery' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-rsv-x-001" + }, + // Non-required parameters + "backupConfig": { + "value": { + "enhancedSecurityState": "Disabled", + "softDeleteFeatureState": "Disabled" + } + }, + "backupPolicies": { + "value": [ + { + "name": "VMpolicy", + "properties": { + "backupManagementType": "AzureIaasVM", + "instantRPDetails": {}, + "instantRpRetentionRangeInDays": 2, + "protectedItemsCount": 0, + "retentionPolicy": { + "dailySchedule": { + "retentionDuration": { + "count": 180, + "durationType": "Days" + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ] + }, + "monthlySchedule": { + "retentionDuration": { + "count": 60, + "durationType": "Months" + }, + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ] + }, + "retentionPolicyType": "LongTermRetentionPolicy", + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionDuration": { + "count": 12, + "durationType": "Weeks" + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ] + }, + "yearlySchedule": { + "monthsOfYear": [ + "January" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + }, + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T07:00:00Z" + ] + } + }, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T07:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "timeZone": "UTC" + } + }, + { + "name": "sqlpolicy", + "properties": { + "backupManagementType": "AzureWorkload", + "protectedItemsCount": 0, + "settings": { + "isCompression": true, + "issqlcompression": true, + "timeZone": "UTC" + }, + "subProtectionPolicy": [ + { + "policyType": "Full", + "retentionPolicy": { + "monthlySchedule": { + "retentionDuration": { + "count": 60, + "durationType": "Months" + }, + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ] + }, + "retentionPolicyType": "LongTermRetentionPolicy", + "weeklySchedule": { + "daysOfTheWeek": [ + "Sunday" + ], + "retentionDuration": { + "count": 104, + "durationType": "Weeks" + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ] + }, + "yearlySchedule": { + "monthsOfYear": [ + "January" + ], + "retentionDuration": { + "count": 10, + "durationType": "Years" + }, + "retentionScheduleFormatType": "Weekly", + "retentionScheduleWeekly": { + "daysOfTheWeek": [ + "Sunday" + ], + "weeksOfTheMonth": [ + "First" + ] + }, + "retentionTimes": [ + "2019-11-07T22:00:00Z" + ] + } + }, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunDays": [ + "Sunday" + ], + "scheduleRunFrequency": "Weekly", + "scheduleRunTimes": [ + "2019-11-07T22:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + } + }, + { + "policyType": "Differential", + "retentionPolicy": { + "retentionDuration": { + "count": 30, + "durationType": "Days" + }, + "retentionPolicyType": "SimpleRetentionPolicy" + }, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunDays": [ + "Monday" + ], + "scheduleRunFrequency": "Weekly", + "scheduleRunTimes": [ + "2017-03-07T02:00:00Z" + ], + "scheduleWeeklyFrequency": 0 + } + }, + { + "policyType": "Log", + "retentionPolicy": { + "retentionDuration": { + "count": 15, + "durationType": "Days" + }, + "retentionPolicyType": "SimpleRetentionPolicy" + }, + "schedulePolicy": { + "scheduleFrequencyInMins": 120, + "schedulePolicyType": "LogSchedulePolicy" + } + } + ], + "workLoadType": "SQLDataBase" + } + }, + { + "name": "filesharepolicy", + "properties": { + "backupManagementType": "AzureStorage", + "protectedItemsCount": 0, + "retentionPolicy": { + "dailySchedule": { + "retentionDuration": { + "count": 30, + "durationType": "Days" + }, + "retentionTimes": [ + "2019-11-07T04:30:00Z" + ] + }, + "retentionPolicyType": "LongTermRetentionPolicy" + }, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicy", + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "2019-11-07T04:30:00Z" + ], + "scheduleWeeklyFrequency": 0 + }, + "timeZone": "UTC", + "workloadType": "AzureFileShare" + } + } + ] + }, + "backupStorageConfig": { + "value": { + "crossRegionRestoreFlag": true, + "storageModelType": "GeoRedundant" + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.siterecovery.windowsazure.com" + ] + }, + "service": "AzureSiteRecovery", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/deploy.bicep new file mode 100644 index 0000000..f2b7589 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/deploy.bicep @@ -0,0 +1,61 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Required. The recovery location the fabric represents.') +param location string = resourceGroup().location + +@description('Optional. The name of the fabric.') +param name string = location + +@description('Optional. Replication containers to create.') +param replicationContainers array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}-rsvPolicy' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource replicationFabric 'Microsoft.RecoveryServices/vaults/replicationFabrics@2022-02-01' = { + name: '${recoveryVaultName}/${name}' + properties: { + customDetails: { + instanceType: 'Azure' + location: location + } + } +} + +module fabric_replicationContainers 'replicationProtectionContainers/deploy.bicep' = [for (container, index) in replicationContainers: { + name: '${deployment().name}-RCont-${index}' + params: { + name: container.name + recoveryVaultName: recoveryVaultName + replicationFabricName: name + replicationContainerMappings: contains(container, 'replicationContainerMappings') ? container.replicationContainerMappings : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + replicationFabric + ] +}] + +@description('The name of the replication fabric.') +output name string = replicationFabric.name + +@description('The resource ID of the replication fabric.') +output resourceId string = replicationFabric.id + +@description('The name of the resource group the replication fabric was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/readme.md b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/readme.md new file mode 100644 index 0000000..928952d --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/readme.md @@ -0,0 +1,103 @@ +# RecoveryServices Vaults ReplicationFabrics `[Microsoft.RecoveryServices/vaults/replicationFabrics]` + +This module deploys a Replication Fabric for Azure to Azure disaster recovery scenario of Azure Site Recovery. + +> Note: this module currently support only the `instanceType: 'Azure'` scenario. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/replicationFabrics` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `location` | string | `[resourceGroup().location]` | The recovery location the fabric represents. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `[parameters('location')]` | The name of the fabric. | +| `replicationContainers` | array | `[]` | Replication containers to create. | + + +### Parameter Usage: `replicationContainers` + +

+ +Parameter JSON format + +```json +"replicationContainers": { + "value": [ + { + "name": "we-container1", + "replicationContainerMappings": [ //optional + { + "policyName": "Default_values", + "targetContainerName": "we-container2" + } + ] + }, + { + "name": "we-container2" + }, + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +replicationContainers: [ + { + name: 'we-container1' + replicationContainerMappings: [ //optional + { + policyName: 'Default_values' + targetContainerName: 'we-container2' + } + ] + } + { + name: 'we-container2' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication fabric. | +| `resourceGroupName` | string | The name of the resource group the replication fabric was created in. | +| `resourceId` | string | The resource ID of the replication fabric. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/deploy.bicep new file mode 100644 index 0000000..c6a4e84 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/deploy.bicep @@ -0,0 +1,67 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Conditional. The name of the parent Replication Fabric. Required if the template is used in a standalone deployment.') +param replicationFabricName string + +@description('Required. The name of the replication container.') +param name string + +@description('Optional. Replication containers mappings to create.') +param replicationContainerMappings array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}-rsvPolicy' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource replicationContainer 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers@2022-02-01' = { + name: '${recoveryVaultName}/${replicationFabricName}/${name}' + properties: { + providerSpecificInput: [ + { + instanceType: 'A2A' + } + ] + } +} + +module fabric_container_containerMappings 'replicationProtectionContainerMappings/deploy.bicep' = [for (mapping, index) in replicationContainerMappings: { + name: '${deployment().name}-Map-${index}' + params: { + name: contains(mapping, 'name') ? mapping.name : '' + policyId: contains(mapping, 'policyId') ? mapping.policyId : '' + policyName: contains(mapping, 'policyName') ? mapping.policyName : '' + recoveryVaultName: recoveryVaultName + replicationFabricName: replicationFabricName + sourceProtectionContainerName: name + targetProtectionContainerId: contains(mapping, 'targetProtectionContainerId') ? mapping.targetProtectionContainerId : '' + targetContainerFabricName: contains(mapping, 'targetContainerFabricName') ? mapping.targetContainerFabricName : replicationFabricName + targetContainerName: contains(mapping, 'targetContainerName') ? mapping.targetContainerName : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + replicationContainer + ] +}] + +@description('The name of the replication container.') +output name string = replicationContainer.name + +@description('The resource ID of the replication container.') +output resourceId string = replicationContainer.id + +@description('The name of the resource group the replication container was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/readme.md b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/readme.md new file mode 100644 index 0000000..eee3ee2 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/readme.md @@ -0,0 +1,98 @@ +# RecoveryServices Vaults ReplicationFabrics ReplicationProtectionContainers `[Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers]` + +This module deploys a Replication Protection Container. + +> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers) | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication container. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | +| `replicationFabricName` | string | The name of the parent Replication Fabric. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `replicationContainerMappings` | array | `[]` | Replication containers mappings to create. | + + +### Parameter Usage: `replicationContainerMappings` + +

+ +Parameter JSON format + +```json +"replicationContainerMappings": { + "value": [ + { + "targetProtectionContainerId": "/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-dr-001/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container1", + "policyId": "/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-dr-001/replicationPolicies/Default_values" + }, + { + "name": null, //Optional + "policyName": "Default_values", + "targetContainerFabricName": "WestEurope", + "targetContainerName": "we-container" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +replicationContainerMappings: [ + { + targetProtectionContainerId: '/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-dr-001/replicationFabrics/NorthEurope/replicationProtectionContainers/ne-container1' + policyId: '/Subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.RecoveryServices/vaults/<>-az-rsv-dr-001/replicationPolicies/Default_values' + } + { + name: null //Optional + policyName: 'Default_values' + targetContainerFabricName: 'WestEurope' + targetContainerName: 'we-container' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication container. | +| `resourceGroupName` | string | The name of the resource group the replication container was created in. | +| `resourceId` | string | The resource ID of the replication container. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/deploy.bicep new file mode 100644 index 0000000..19c3258 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/deploy.bicep @@ -0,0 +1,65 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Conditional. The name of the parent Replication Fabric. Required if the template is used in a standalone deployment.') +param replicationFabricName string + +@description('Conditional. The name of the parent source Replication container. Required if the template is used in a standalone deployment.') +param sourceProtectionContainerName string + +@description('Optional. Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored.') +param targetProtectionContainerId string = '' + +@description('Optional. Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored.') +param targetContainerFabricName string = replicationFabricName + +@description('Optional. Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored.') +param targetContainerName string = '' + +@description('Optional. Resource ID of the replication policy. If defined, policyName will be ignored.') +param policyId string = '' + +@description('Optional. Name of the replication policy. Will be ignored if policyId is also specified.') +param policyName string = '' + +@description('Optional. The name of the replication container mapping. If not provided, it will be automatically generated as `-`.') +param name string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var policyResourceId = policyId != '' ? policyId : subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationPolicies', recoveryVaultName, policyName) +var targetProtectionContainerResourceId = targetProtectionContainerId != '' ? targetProtectionContainerId : subscriptionResourceId('Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers', recoveryVaultName, targetContainerFabricName, targetContainerName) +var mappingName = !empty(name) ? name : '${sourceProtectionContainerName}-${split(targetProtectionContainerResourceId, '/')[10]}' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}-rsvPolicy' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource replicationContainer 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings@2022-02-01' = { + name: '${recoveryVaultName}/${replicationFabricName}/${sourceProtectionContainerName}/${mappingName}' + properties: { + targetProtectionContainerId: targetProtectionContainerResourceId + policyId: policyResourceId + providerSpecificInput: { + instanceType: 'A2A' + } + } +} + +@description('The name of the replication container.') +output name string = replicationContainer.name + +@description('The resource ID of the replication container.') +output resourceId string = replicationContainer.id + +@description('The name of the resource group the replication container was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/readme.md b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/readme.md new file mode 100644 index 0000000..4bcee93 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/readme.md @@ -0,0 +1,51 @@ +# RecoveryServices Vaults ReplicationFabrics ReplicationProtectionContainers ReplicationProtectionContainerMappings `[Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings]` + +This module deploys a Replication Protection Container Mapping. + +> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | +| `replicationFabricName` | string | The name of the parent Replication Fabric. Required if the template is used in a standalone deployment. | +| `sourceProtectionContainerName` | string | The name of the parent source Replication container. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `''` | The name of the replication container mapping. If not provided, it will be automatically generated as `-`. | +| `policyId` | string | `''` | Resource ID of the replication policy. If defined, policyName will be ignored. | +| `policyName` | string | `''` | Name of the replication policy. Will be ignored if policyId is also specified. | +| `targetContainerFabricName` | string | `[parameters('replicationFabricName')]` | Name of the fabric containing the target container. If targetProtectionContainerId is specified, this parameter will be ignored. | +| `targetContainerName` | string | `''` | Name of the target container. Must be specified if targetProtectionContainerId is not. If targetProtectionContainerId is specified, this parameter will be ignored. | +| `targetProtectionContainerId` | string | `''` | Resource ID of the target Replication container. Must be specified if targetContainerName is not. If specified, targetContainerFabricName and targetContainerName will be ignored. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication container. | +| `resourceGroupName` | string | The name of the resource group the replication container was created in. | +| `resourceId` | string | The resource ID of the replication container. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/version.json b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/version.json b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/version.json b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationFabrics/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/deploy.bicep b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/deploy.bicep new file mode 100644 index 0000000..2e98ec3 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/deploy.bicep @@ -0,0 +1,57 @@ +@description('Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment.') +param recoveryVaultName string + +@description('Required. The name of the replication policy.') +param name string + +@description('Optional. The app consistent snapshot frequency (in minutes).') +param appConsistentFrequencyInMinutes int = 60 + +@description('Optional. The crash consistent snapshot frequency (in minutes).') +param crashConsistentFrequencyInMinutes int = 5 + +@description('Optional. A value indicating whether multi-VM sync has to be enabled.') +@allowed([ + 'Enable' + 'Disable' +]) +param multiVmSyncStatus string = 'Enable' + +@description('Optional. The duration in minutes until which the recovery points need to be stored.') +param recoveryPointHistory int = 1440 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}-rsvPolicy' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource replicationPolicy 'Microsoft.RecoveryServices/vaults/replicationPolicies@2022-02-01' = { + name: '${recoveryVaultName}/${name}' + properties: { + providerSpecificInput: { + instanceType: 'A2A' + appConsistentFrequencyInMinutes: appConsistentFrequencyInMinutes + crashConsistentFrequencyInMinutes: crashConsistentFrequencyInMinutes + multiVmSyncStatus: multiVmSyncStatus + recoveryPointHistory: recoveryPointHistory + } + } +} +@description('The name of the replication policy.') +output name string = replicationPolicy.name + +@description('The resource ID of the replication policy.') +output resourceId string = replicationPolicy.id + +@description('The name of the resource group the replication policy was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/readme.md b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/readme.md new file mode 100644 index 0000000..f00e7a5 --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/readme.md @@ -0,0 +1,52 @@ +# RecoveryServices Vaults ReplicationPolicies `[Microsoft.RecoveryServices/vaults/replicationPolicies]` + +This module deploys a Replication Policy for Disaster Recovery scenario. + +> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2022-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-02-01/vaults/replicationPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication policy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `recoveryVaultName` | string | The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `appConsistentFrequencyInMinutes` | int | `60` | | The app consistent snapshot frequency (in minutes). | +| `crashConsistentFrequencyInMinutes` | int | `5` | | The crash consistent snapshot frequency (in minutes). | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `multiVmSyncStatus` | string | `'Enable'` | `[Disable, Enable]` | A value indicating whether multi-VM sync has to be enabled. | +| `recoveryPointHistory` | int | `1440` | | The duration in minutes until which the recovery points need to be stored. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the replication policy. | +| `resourceGroupName` | string | The name of the resource group the replication policy was created in. | +| `resourceId` | string | The resource ID of the replication policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/version.json b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/replicationPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.RecoveryServices/vaults/version.json b/modules/Microsoft.RecoveryServices/vaults/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.RecoveryServices/vaults/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Resources/deploymentScripts/.test/cli.parameters.json b/modules/Microsoft.Resources/deploymentScripts/.test/cli.parameters.json new file mode 100644 index 0000000..e3e775a --- /dev/null +++ b/modules/Microsoft.Resources/deploymentScripts/.test/cli.parameters.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ds-cli-001" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "kind": { + "value": "AzureCLI" + }, + "azCliVersion": { + "value": "2.15.0" + }, + "scriptContent": { + "value": "echo \"Hello from inside the script\"" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "cleanupPreference": { + "value": "Always" + }, + "timeout": { + "value": "PT30M" + } + } +} diff --git a/modules/Microsoft.Resources/deploymentScripts/.test/ps.parameters.json b/modules/Microsoft.Resources/deploymentScripts/.test/ps.parameters.json new file mode 100644 index 0000000..accc14b --- /dev/null +++ b/modules/Microsoft.Resources/deploymentScripts/.test/ps.parameters.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-ds-ps-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "kind": { + "value": "AzurePowerShell" + }, + "azPowerShellVersion": { + "value": "3.0" + }, + "scriptContent": { + "value": "Write-Host 'Running PowerShell from template'" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "cleanupPreference": { + "value": "Always" + }, + "timeout": { + "value": "PT30M" + } + } +} diff --git a/modules/Microsoft.Resources/deploymentScripts/deploy.bicep b/modules/Microsoft.Resources/deploymentScripts/deploy.bicep new file mode 100644 index 0000000..823f3a2 --- /dev/null +++ b/modules/Microsoft.Resources/deploymentScripts/deploy.bicep @@ -0,0 +1,139 @@ +@description('Required. Display name of the script to be run.') +param name string + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Type of the script. AzurePowerShell, AzureCLI.') +@allowed([ + 'AzurePowerShell' + 'AzureCLI' +]) +param kind string = 'AzurePowerShell' + +@description('Optional. Azure PowerShell module version to be used.') +param azPowerShellVersion string = '3.0' + +@description('Optional. Azure CLI module version to be used.') +param azCliVersion string = '' + +@description('Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead.') +param scriptContent string = '' + +@description('Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent instead.') +param primaryScriptUri string = '' + +@description('Optional. The environment variables to pass over to the script. Must have a \'name\' and a \'value\' or a \'secretValue\' property.') +param environmentVariables array = [] + +@description('Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent).') +param supportingScriptUris array = [] + +@description('Optional. Command-line arguments to pass to the script. Arguments are separated by spaces.') +param arguments string = '' + +@description('Optional. Interval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P7D means one week).') +param retentionInterval string = 'P1D' + +@description('Optional. When set to false, script will run every time the template is deployed. When set to true, the script will only run once.') +param runOnce bool = false + +@description('Optional. The clean up preference when the script execution gets in a terminal state. Specify the preference on when to delete the deployment script resources. The default value is Always, which means the deployment script resources are deleted despite the terminal state (Succeeded, Failed, canceled).') +@allowed([ + 'Always' + 'OnSuccess' + 'OnExpiration' +]) +param cleanupPreference string = 'Always' + +@description('Optional. Container group name, if not specified then the name will get auto-generated. Not specifying a \'containerGroupName\' indicates the system to generate a unique name which might end up flagging an Azure Policy as non-compliant. Use \'containerGroupName\' when you have an Azure Policy that expects a specific naming convention or when you want to fully control the name. \'containerGroupName\' property must be between 1 and 63 characters long, must contain only lowercase letters, numbers, and dashes and it cannot start or end with a dash and consecutive dashes are not allowed.') +param containerGroupName string = '' + +@description('Optional. Maximum allowed script execution time specified in ISO 8601 format. Default value is PT1H - 1 hour; \'PT30M\' - 30 minutes; \'P5D\' - 5 days; \'P1Y\' 1 year.') +param timeout string = 'PT1H' + +@description('Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed.') +param baseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var containerSettings = { + containerGroupName: containerGroupName +} + +var identityType = !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: name + location: location + tags: tags + identity: identity + kind: any(kind) + properties: { + azPowerShellVersion: kind == 'AzurePowerShell' ? azPowerShellVersion : null + azCliVersion: kind == 'AzureCLI' ? azCliVersion : null + containerSettings: empty(containerGroupName) ? null : containerSettings + arguments: arguments + environmentVariables: empty(environmentVariables) ? null : environmentVariables + scriptContent: empty(scriptContent) ? null : scriptContent + primaryScriptUri: empty(primaryScriptUri) ? null : primaryScriptUri + supportingScriptUris: empty(supportingScriptUris) ? null : supportingScriptUris + cleanupPreference: cleanupPreference + forceUpdateTag: runOnce ? resourceGroup().name : baseTime + retentionInterval: retentionInterval + timeout: timeout + } +} + +resource deploymentScript_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${deploymentScript.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: deploymentScript +} + +@description('The resource ID of the deployment script.') +output resourceId string = deploymentScript.id + +@description('The resource group the deployment script was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployment script.') +output name string = deploymentScript.name + +@description('The location the resource was deployed into.') +output location string = deploymentScript.location diff --git a/modules/Microsoft.Resources/deploymentScripts/readme.md b/modules/Microsoft.Resources/deploymentScripts/readme.md new file mode 100644 index 0000000..37d74a8 --- /dev/null +++ b/modules/Microsoft.Resources/deploymentScripts/readme.md @@ -0,0 +1,310 @@ +# Deployment Scripts `[Microsoft.Resources/deploymentScripts]` + +This module deploys a deployment script. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Resources/deploymentScripts` | [2020-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Resources/2020-10-01/deploymentScripts) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Display name of the script to be run. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `arguments` | string | `''` | | Command-line arguments to pass to the script. Arguments are separated by spaces. | +| `azCliVersion` | string | `''` | | Azure CLI module version to be used. | +| `azPowerShellVersion` | string | `'3.0'` | | Azure PowerShell module version to be used. | +| `cleanupPreference` | string | `'Always'` | `[Always, OnExpiration, OnSuccess]` | The clean up preference when the script execution gets in a terminal state. Specify the preference on when to delete the deployment script resources. The default value is Always, which means the deployment script resources are deleted despite the terminal state (Succeeded, Failed, canceled). | +| `containerGroupName` | string | `''` | | Container group name, if not specified then the name will get auto-generated. Not specifying a 'containerGroupName' indicates the system to generate a unique name which might end up flagging an Azure Policy as non-compliant. Use 'containerGroupName' when you have an Azure Policy that expects a specific naming convention or when you want to fully control the name. 'containerGroupName' property must be between 1 and 63 characters long, must contain only lowercase letters, numbers, and dashes and it cannot start or end with a dash and consecutive dashes are not allowed. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `environmentVariables` | array | `[]` | | The environment variables to pass over to the script. Must have a 'name' and a 'value' or a 'secretValue' property. | +| `kind` | string | `'AzurePowerShell'` | `[AzureCLI, AzurePowerShell]` | Type of the script. AzurePowerShell, AzureCLI. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `primaryScriptUri` | string | `''` | | Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent instead. | +| `retentionInterval` | string | `'P1D'` | | Interval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P7D means one week). | +| `runOnce` | bool | `False` | | When set to false, script will run every time the template is deployed. When set to true, the script will only run once. | +| `scriptContent` | string | `''` | | Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead. | +| `supportingScriptUris` | array | `[]` | | List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent). | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timeout` | string | `'PT1H'` | | Maximum allowed script execution time specified in ISO 8601 format. Default value is PT1H - 1 hour; 'PT30M' - 30 minutes; 'P5D' - 5 days; 'P1Y' 1 year. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('yyyy-MM-dd-HH-mm-ss')]` | Do not provide a value! This date value is used to make sure the script run every time the template is deployed. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployment script. | +| `resourceGroupName` | string | The resource group the deployment script was deployed into. | +| `resourceId` | string | The resource ID of the deployment script. | + +## Considerations + +This module requires a User Assigned Identity (MSI, managed service identity) to exist, and this MSI has to have contributor rights on the subscription - that allows the Deployment Script to create the required Storage Account and the Azure Container Instance. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Cli

+ +
+ +via Bicep module + +```bicep +module deploymentScripts './Microsoft.Resources/deploymentScripts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DeploymentScripts' + params: { + // Required parameters + name: '<>-az-ds-cli-001' + // Non-required parameters + azCliVersion: '2.15.0' + cleanupPreference: 'Always' + kind: 'AzureCLI' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'echo \'Hello from inside the script\'' + timeout: 'PT30M' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-ds-cli-001" + }, + // Non-required parameters + "azCliVersion": { + "value": "2.15.0" + }, + "cleanupPreference": { + "value": "Always" + }, + "kind": { + "value": "AzureCLI" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "scriptContent": { + "value": "echo \"Hello from inside the script\"" + }, + "timeout": { + "value": "PT30M" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Ps

+ +
+ +via Bicep module + +```bicep +module deploymentScripts './Microsoft.Resources/deploymentScripts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-DeploymentScripts' + params: { + // Required parameters + name: '<>-az-ds-ps-001' + // Non-required parameters + azPowerShellVersion: '3.0' + cleanupPreference: 'Always' + kind: 'AzurePowerShell' + lock: 'CanNotDelete' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'Write-Host 'Running PowerShell from template'' + timeout: 'PT30M' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-ds-ps-001" + }, + // Non-required parameters + "azPowerShellVersion": { + "value": "3.0" + }, + "cleanupPreference": { + "value": "Always" + }, + "kind": { + "value": "AzurePowerShell" + }, + "lock": { + "value": "CanNotDelete" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "scriptContent": { + "value": "Write-Host 'Running PowerShell from template'" + }, + "timeout": { + "value": "PT30M" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Resources/deploymentScripts/version.json b/modules/Microsoft.Resources/deploymentScripts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Resources/deploymentScripts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..f6649ed --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,223 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'AcrDelete': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11') + 'AcrImageSigner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f') + 'AcrPull': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + 'AcrPush': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec') + 'AcrQuarantineReader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04') + 'AcrQuarantineWriter': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608') + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'App Configuration Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b') + 'App Configuration Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Attestation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bbf86eb8-f7b4-4cce-96e4-18cddf81d86e') + 'Attestation Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fd1bd22b-8476-40bc-a0bc-69b95687b9f3') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Connected Machine Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b64e21ea-ac4e-4cdf-9dc9-5b892992bee7') + 'Azure Connected Machine Resource Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cd570a14-e51a-42ad-bac8-bafd67325302') + 'Azure Digital Twins Owner (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bcd981a7-7f74-457b-83e1-cceb9e632ffe') + 'Azure Digital Twins Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd57506d4-4c8d-48b1-8587-93c323f6a5a3') + 'Azure Event Hubs Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f526a384-b230-433a-b45c-95f59c4a2dec') + 'Azure Event Hubs Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a638d3c7-ab3a-418d-83e6-5f17a39d4fde') + 'Azure Event Hubs Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2b629674-e913-4c01-ae53-ef4638d8f975') + 'Azure Kubernetes Service Cluster Admin Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8') + 'Azure Kubernetes Service Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4abbcc35-e782-43d8-92c5-2d3f1bd2253f') + 'Azure Kubernetes Service Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8') + 'Azure Maps Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204') + 'Azure Maps Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '423170ca-a8f6-4b0f-8487-9e4eb8f49bfa') + 'Azure Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Azure Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Azure Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Azure Service Bus Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + 'Azure Service Bus Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0') + 'Azure Service Bus Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + 'Azure Stack Registration Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6f12a6df-dd06-4f3e-bcb1-ce8be600526a') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'Backup Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a795c7a0-d4a2-40c1-ae25-d81f01202912') + 'Billing Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'Blockchain Member Node Access (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '31a002a1-acaf-453e-8a5b-297c9ca1ea24') + 'Blueprint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '41077137-e803-4205-871c-5a86e6a753b4') + 'Blueprint Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '437d2ced-4a38-4302-8479-ed2bcb43d090') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '985d6b00-f706-48f5-a6fe-d0ca12fb668d') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services Custom Vision Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3') + 'Cognitive Services Custom Vision Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f') + 'Cognitive Services Custom Vision Labeler': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c') + 'Cognitive Services Custom Vision Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73') + 'Cognitive Services Custom Vision Trainer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b') + 'Cognitive Services Data Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c') + 'Cognitive Services QnA Maker Editor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025') + 'Cognitive Services QnA Maker Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'CosmosBackupOperator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db7b14f2-5adf-42da-9f96-f2ee17bab5cb') + 'Cost Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434105ed-43f6-45c7-a02f-909b2ba83430') + 'Cost Management Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '72fafb9e-0641-4937-9268-a91bfd8191a3') + 'Data Box Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'add466c9-e687-43fc-8d98-dfcf8d720be5') + 'Data Box Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'EventGrid EventSubscription Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405') + 'Experimentation Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a33b-edd6ce5c915c') + 'Experimentation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a22b-edd6ce5c915c') + 'Experimentation Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49632ef5-d9ac-41f4-b8e7-bbe587fa74a1') + 'FHIR Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5a1fc7df-4bf1-4951-a576-89034ee01acd') + 'FHIR Data Exporter': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3db33094-8700-4567-8da5-1501d4e7e843') + 'FHIR Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4c8d0bbc-75d3-4935-991f-5f3c56d81508') + 'FHIR Data Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3f88fce4-5892-4214-ae73-ba5294559913') + 'Graph Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b60367af-1334-4454-b71e-769d9a4f83d9') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'HDInsight Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d8d5a11-05d3-4bda-a417-a08778121c7c') + 'Hierarchy Settings Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '350f8d15-c687-4448-8ae1-157740a3936d') + 'Hybrid Server Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb') + 'Hybrid Server Resource Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '48b40c6e-82e0-4eb3-90d5-19e40f49b624') + 'Integration Service Environment Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a41e2c5b-bd99-4a07-88f4-9bf657a760b8') + 'Integration Service Environment Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7aa55d3-1abb-444a-a5ca-5e51e485d6ec') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Knowledge Consumer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ee361c5d-f7b5-4119-b4b6-892157c8f64c') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + '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') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Managed Services Registration assignment Delete ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '91c1777a-f3dc-4fae-b103-61d183457e46') + 'Management Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c') + 'Management Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d') + 'Marketplace Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dd920d6d-f481-47f1-b461-f338c46b2d9f') + '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') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + 'Object Understanding Account Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4dd61c23-6743-42fe-a388-d8bdd41cb745') + 'Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Policy Insights Data Writer (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '66bb4e9e-b016-4a94-8249-4c0511c2be84') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Reader and Data Access': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Remote Rendering Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3df8b902-2a6f-47c7-8cc5-360e9b272a7e') + 'Remote Rendering Client': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd39065c4-c120-43c9-ab0a-63eed9795f0a') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Assessment Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '612c2aa1-cb24-443b-ac28-3ab7272de6f5') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR AccessKey Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e') + 'SignalR Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Site Recovery Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dbaa88c4-0c30-4179-9fb3-46319faa6149') + 'Spatial Anchors Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827') + 'Spatial Anchors Account Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '70bbe301-9835-447d-afdd-19eb3167307c') + 'Spatial Anchors Account Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d51204f-eb77-4b1c-b86a-2ec626c49413') + 'SQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'SQL Server Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12') + 'Storage Blob Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + 'Storage Blob Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b') + 'Storage Blob Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1') + 'Storage Blob Delegator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a') + 'Storage File Data SMB Share Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb') + 'Storage File Data SMB Share Elevated Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7') + 'Storage File Data SMB Share Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314') + 'Storage Queue Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + 'Storage Queue Data Message Processor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed') + 'Storage Queue Data Message Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a') + 'Storage Queue Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925') + 'Support Request Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(last(split(resourceId, '/')), principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } +}] diff --git a/modules/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep b/modules/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep new file mode 100644 index 0000000..7371d44 --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + diff --git a/modules/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep b/modules/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep new file mode 100644 index 0000000..24f5d3e --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for a testing purposes') +@maxLength(90) +param resourceGroupName string = 'ms.resources.resourcegroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to') +param location 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 = 'rrgcom' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Test: 'Yes' + } + } +} diff --git a/modules/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep b/modules/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep new file mode 100644 index 0000000..21c2684 --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep @@ -0,0 +1,18 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@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 = 'rrgmin' + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + } +} diff --git a/modules/Microsoft.Resources/resourceGroups/deploy.bicep b/modules/Microsoft.Resources/resourceGroups/deploy.bicep new file mode 100644 index 0000000..461761e --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/deploy.bicep @@ -0,0 +1,76 @@ +targetScope = 'subscription' + +@description('Required. The name of the Resource Group.') +param name string + +@description('Optional. Location of the Resource Group. It uses the deployment\'s location when not provided.') +param location string = deployment().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the storage account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2019-05-01' = { + location: location + name: name + tags: tags + properties: {} +} + +module resourceGroup_lock '../../Microsoft.Authorization/locks/resourceGroup/deploy.bicep' = if (!empty(lock)) { + name: '${uniqueString(deployment().name, location)}-${lock}-Lock' + params: { + level: any(lock) + name: '${resourceGroup.name}-${lock}-lock' + } + scope: resourceGroup +} + +module resourceGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-RG-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: resourceGroup.id + } + scope: resourceGroup +}] + +@description('The name of the resource group.') +output name string = resourceGroup.name + +@description('The resource ID of the resource group.') +output resourceId string = resourceGroup.id + +@description('The location the resource was deployed into.') +output location string = resourceGroup.location diff --git a/modules/Microsoft.Resources/resourceGroups/readme.md b/modules/Microsoft.Resources/resourceGroups/readme.md new file mode 100644 index 0000000..465d3fe --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/readme.md @@ -0,0 +1,272 @@ +# Resource Groups `[Microsoft.Resources/resourceGroups]` + +This module deploys a resource group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Resources/resourceGroups` | [2019-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Resources/2019-05-01/resourceGroups) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Resource Group. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | | Location of the Resource Group. It uses the deployment's location when not provided. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | 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}` | | Tags of the storage account resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Considerations + +This module requires a User Assigned Identity (MSI, managed service identity) to exist, and this MSI has to have contributor rights on the subscription - that allows the Deployment Script to create the required Storage Account and the Azure Container Instance. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the resource group. | +| `resourceId` | string | The resource ID of the resource group. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Authorization/locks/resourceGroup` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module resourceGroups './Microsoft.Resources/resourceGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-rrgcom' + params: { + // Required parameters + name: '<>rrgcom001' + // Non-required parameters + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Test: 'Yes' + } + } +} +``` + +
+

+ +

+ +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": "<>rrgcom001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Test": "Yes" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module resourceGroups './Microsoft.Resources/resourceGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-rrgmin' + params: { + name: '<>rrgmin001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>rrgmin001" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Resources/resourceGroups/version.json b/modules/Microsoft.Resources/resourceGroups/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Resources/resourceGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Resources/tags/.test/min.parameters.json b/modules/Microsoft.Resources/tags/.test/min.parameters.json new file mode 100644 index 0000000..d90c44f --- /dev/null +++ b/modules/Microsoft.Resources/tags/.test/min.parameters.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} +} diff --git a/modules/Microsoft.Resources/tags/.test/rg.parameters.json b/modules/Microsoft.Resources/tags/.test/rg.parameters.json new file mode 100644 index 0000000..a90e2e5 --- /dev/null +++ b/modules/Microsoft.Resources/tags/.test/rg.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "onlyUpdate": { + "value": false + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + }, + "resourceGroupName": { + "value": "validation-rg" + } + } +} diff --git a/modules/Microsoft.Resources/tags/.test/sub.parameters.json b/modules/Microsoft.Resources/tags/.test/sub.parameters.json new file mode 100644 index 0000000..840b23b --- /dev/null +++ b/modules/Microsoft.Resources/tags/.test/sub.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "onlyUpdate": { + "value": true + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + } + } +} diff --git a/modules/Microsoft.Resources/tags/deploy.bicep b/modules/Microsoft.Resources/tags/deploy.bicep new file mode 100644 index 0000000..146c2c5 --- /dev/null +++ b/modules/Microsoft.Resources/tags/deploy.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@description('Optional. Name of the Resource Group to assign the tags to. If no Resource Group name is provided, and Subscription ID is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription.') +param resourceGroupName string = '' + +@description('Optional. Subscription ID of the subscription to assign the tags to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription.') +param subscriptionId string = subscription().id + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module tags_sub 'subscriptions/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${deployment().name}-Tags-Sub' + params: { + onlyUpdate: onlyUpdate + tags: tags + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module tags_rg 'resourceGroups/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${deployment().name}-Tags-RG' + scope: resourceGroup(resourceGroupName) + params: { + onlyUpdate: onlyUpdate + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the tags resource.') +output name string = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.name : tags_sub.outputs.name + +@description('The applied tags.') +output tags object = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.tags : tags_sub.outputs.tags + +@description('The resource ID of the applied tags.') +output resourceId string = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.resourceId : tags_sub.outputs.resourceId diff --git a/modules/Microsoft.Resources/tags/readme.md b/modules/Microsoft.Resources/tags/readme.md new file mode 100644 index 0000000..cc35a94 --- /dev/null +++ b/modules/Microsoft.Resources/tags/readme.md @@ -0,0 +1,221 @@ +# Resources Tags `[Microsoft.Resources/tags]` + +This module deploys Resources Tags on a subscription or resource group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2019-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Resources/2019-10-01/tags) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `resourceGroupName` | string | `''` | Name of the Resource Group to assign the tags to. If no Resource Group name is provided, and Subscription ID is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription. | +| `subscriptionId` | string | `[subscription().id]` | Subscription ID of the subscription to assign the tags to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Tags' + params: { + + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} +} +``` + +
+

+ +

Example 2: Rg

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Tags' + params: { + onlyUpdate: false + resourceGroupName: 'validation-rg' + tags: { + Test: 'Yes' + TestToo: 'No' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "onlyUpdate": { + "value": false + }, + "resourceGroupName": { + "value": "validation-rg" + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + } + } +} +``` + +
+

+ +

Example 3: Sub

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Tags' + params: { + onlyUpdate: true + tags: { + Test: 'Yes' + TestToo: 'No' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "onlyUpdate": { + "value": true + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep b/modules/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep new file mode 100644 index 0000000..0f3301f --- /dev/null +++ b/modules/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep @@ -0,0 +1,9 @@ +@description('Optional. The name of the tags resource.') +param name string = 'default' + +resource tags 'Microsoft.Resources/tags@2019-10-01' existing = { + name: name +} + +@description('Tags currently applied to the subscription level') +output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} diff --git a/modules/Microsoft.Resources/tags/resourceGroups/deploy.bicep b/modules/Microsoft.Resources/tags/resourceGroups/deploy.bicep new file mode 100644 index 0000000..d402bcf --- /dev/null +++ b/modules/Microsoft.Resources/tags/resourceGroups/deploy.bicep @@ -0,0 +1,48 @@ +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. The name of the tags resource.') +param name string = 'default' + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module readTags '.bicep/readTags.bicep' = if (onlyUpdate) { + name: '${deployment().name}-ReadTags' +} + +var newTags = (onlyUpdate) ? union(readTags.outputs.existingTags, tags) : tags + +resource tag 'Microsoft.Resources/tags@2019-10-01' = { + name: name + properties: { + tags: newTags + } +} + +@description('The name of the tags resource.') +output name string = tag.name + +@description('The resource ID of the applied tags.') +output resourceId string = tag.id + +@description('The name of the resource group the tags were applied to.') +output resourceGroupName string = resourceGroup().name + +@description('The applied tags.') +output tags object = newTags diff --git a/modules/Microsoft.Resources/tags/resourceGroups/readme.md b/modules/Microsoft.Resources/tags/resourceGroups/readme.md new file mode 100644 index 0000000..7270c4d --- /dev/null +++ b/modules/Microsoft.Resources/tags/resourceGroups/readme.md @@ -0,0 +1,81 @@ +# Resources Tags ResourceGroups `[Microsoft.Resources/tags/resourceGroups]` + +This module deploys Resources Tags on a resource group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2019-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Resources/2019-10-01/tags) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | The name of the tags resource. | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceGroupName` | string | The name of the resource group the tags were applied to. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Resources/tags/resourceGroups/version.json b/modules/Microsoft.Resources/tags/resourceGroups/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Resources/tags/resourceGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep b/modules/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep new file mode 100644 index 0000000..65b2457 --- /dev/null +++ b/modules/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep @@ -0,0 +1,11 @@ +targetScope = 'subscription' + +@description('Optional. The name of the tags resource.') +param name string = 'default' + +resource tags 'Microsoft.Resources/tags@2019-10-01' existing = { + name: name +} + +@description('Tags currently applied to the subscription level') +output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} diff --git a/modules/Microsoft.Resources/tags/subscriptions/deploy.bicep b/modules/Microsoft.Resources/tags/subscriptions/deploy.bicep new file mode 100644 index 0000000..d72b0fb --- /dev/null +++ b/modules/Microsoft.Resources/tags/subscriptions/deploy.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. The name of the tags resource.') +param name string = 'default' + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module readTags '.bicep/readTags.bicep' = if (onlyUpdate) { + name: '${deployment().name}-ReadTags' +} + +var newTags = (onlyUpdate) ? union(readTags.outputs.existingTags, tags) : tags + +resource tag 'Microsoft.Resources/tags@2019-10-01' = { + name: name + properties: { + tags: newTags + } +} + +@description('The name of the tags resource.') +output name string = tag.name + +@description('The applied tags.') +output tags object = newTags + +@description('The resource ID of the applied tags.') +output resourceId string = tag.id diff --git a/modules/Microsoft.Resources/tags/subscriptions/readme.md b/modules/Microsoft.Resources/tags/subscriptions/readme.md new file mode 100644 index 0000000..e3129f2 --- /dev/null +++ b/modules/Microsoft.Resources/tags/subscriptions/readme.md @@ -0,0 +1,81 @@ +# Resources Tags Subscriptions `[Microsoft.Resources/tags/subscriptions]` + +This module deploys Resources Tags on a subscription scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2019-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Resources/2019-10-01/tags) | + +## Parameters + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `name` | string | `'default'` | The name of the tags resource. | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Resources/tags/subscriptions/version.json b/modules/Microsoft.Resources/tags/subscriptions/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Resources/tags/subscriptions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Resources/tags/version.json b/modules/Microsoft.Resources/tags/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Resources/tags/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Security/azureSecurityCenter/.bicep/nested_iotSecuritySolutions.bicep b/modules/Microsoft.Security/azureSecurityCenter/.bicep/nested_iotSecuritySolutions.bicep new file mode 100644 index 0000000..5f37462 --- /dev/null +++ b/modules/Microsoft.Security/azureSecurityCenter/.bicep/nested_iotSecuritySolutions.bicep @@ -0,0 +1,16 @@ +@description('Optional. Security Solution data') +param ioTSecuritySolutionProperties object = {} + +resource iotSecuritySolutions 'Microsoft.Security/iotSecuritySolutions@2019-08-01' = if (!empty(ioTSecuritySolutionProperties)) { + name: 'iotSecuritySolutions' + properties: { + workspace: ioTSecuritySolutionProperties.workspace + displayName: ioTSecuritySolutionProperties.displayName + status: ioTSecuritySolutionProperties.status + export: ioTSecuritySolutionProperties.export + disabledDataSources: ioTSecuritySolutionProperties.disabledDataSources + iotHubs: ioTSecuritySolutionProperties.iotHubs + userDefinedResources: ioTSecuritySolutionProperties.userDefinedResources + recommendationsConfiguration: ioTSecuritySolutionProperties.recommendationsConfiguration + } +} diff --git a/modules/Microsoft.Security/azureSecurityCenter/.test/parameters.json b/modules/Microsoft.Security/azureSecurityCenter/.test/parameters.json new file mode 100644 index 0000000..cfa02a1 --- /dev/null +++ b/modules/Microsoft.Security/azureSecurityCenter/.test/parameters.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "value": "/subscriptions/<>" + }, + "securityContactProperties": { + "value": { + "email": "foo@contoso.com", + "phone": "+12345678", + "alertNotifications": "Off", + "alertsToAdmins": "Off" + } + }, + "workspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + } +} diff --git a/modules/Microsoft.Security/azureSecurityCenter/deploy.bicep b/modules/Microsoft.Security/azureSecurityCenter/deploy.bicep new file mode 100644 index 0000000..5987272 --- /dev/null +++ b/modules/Microsoft.Security/azureSecurityCenter/deploy.bicep @@ -0,0 +1,247 @@ +targetScope = 'subscription' + +@description('Required. The full Azure ID of the workspace to save the data in.') +param workspaceId string + +@description('Required. All the VMs in this scope will send their security data to the mentioned workspace unless overridden by a setting with more specific scope.') +param scope string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Describes what kind of security agent provisioning action to take. - On or Off.') +@allowed([ + 'On' + 'Off' +]) +param autoProvision string = 'On' + +@description('Optional. Device Security group data.') +param deviceSecurityGroupProperties object = {} + +@description('Optional. Security Solution data.') +param ioTSecuritySolutionProperties object = {} + +@description('Optional. The pricing tier value for VMs. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param virtualMachinesPricingTier string = 'Free' + +@description('Optional. The pricing tier value for SqlServers. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param sqlServersPricingTier string = 'Free' + +@description('Optional. The pricing tier value for AppServices. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param appServicesPricingTier string = 'Free' + +@description('Optional. The pricing tier value for StorageAccounts. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param storageAccountsPricingTier string = 'Free' + +@description('Optional. The pricing tier value for SqlServerVirtualMachines. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param sqlServerVirtualMachinesPricingTier string = 'Free' + +@description('Optional. The pricing tier value for KubernetesService. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param kubernetesServicePricingTier string = 'Free' + +@description('Optional. The pricing tier value for ContainerRegistry. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param containerRegistryPricingTier string = 'Free' + +@description('Optional. The pricing tier value for KeyVaults. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param keyVaultsPricingTier string = 'Free' + +@description('Optional. The pricing tier value for DNS. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param dnsPricingTier string = 'Free' + +@description('Optional. The pricing tier value for ARM. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param armPricingTier string = 'Free' + +@description('Optional. The pricing tier value for OpenSourceRelationalDatabases. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param openSourceRelationalDatabasesTier string = 'Free' + +@description('Optional. The pricing tier value for containers. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param containersTier string = 'Free' + +@description('Optional. The pricing tier value for CosmosDbs. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard.') +@allowed([ + 'Free' + 'Standard' +]) +param cosmosDbsTier string = 'Free' + +@description('Optional. Security contact data.') +param securityContactProperties object = {} + +@description('Optional. Location deployment metadata.') +param location string = deployment().location + +var pricings = [ + { + name: 'VirtualMachines' + pricingTier: virtualMachinesPricingTier + } + { + name: 'SqlServers' + pricingTier: sqlServersPricingTier + } + { + name: 'AppServices' + pricingTier: appServicesPricingTier + } + { + name: 'StorageAccounts' + pricingTier: storageAccountsPricingTier + } + { + name: 'SqlServerVirtualMachines' + pricingTier: sqlServerVirtualMachinesPricingTier + } + { + name: 'KubernetesService' + pricingTier: kubernetesServicePricingTier + } + { + name: 'ContainerRegistry' + pricingTier: containerRegistryPricingTier + } + { + name: 'KeyVaults' + pricingTier: keyVaultsPricingTier + } + { + name: 'Dns' + pricingTier: dnsPricingTier + } + { + name: 'Arm' + pricingTier: armPricingTier + } + { + name: 'OpenSourceRelationalDatabases' + pricingTier: openSourceRelationalDatabasesTier + } + { + name: 'Containers' + pricingTier: containersTier + } + { + name: 'CosmosDbs' + pricingTier: cosmosDbsTier + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource pricingTiers 'Microsoft.Security/pricings@2018-06-01' = [for (pricing, index) in pricings: { + name: pricing.name + properties: { + pricingTier: pricing.pricingTier + } +}] + +resource autoProvisioningSettings 'Microsoft.Security/autoProvisioningSettings@2017-08-01-preview' = { + name: 'default' + properties: { + autoProvision: autoProvision + } +} + +resource deviceSecurityGroups 'Microsoft.Security/deviceSecurityGroups@2019-08-01' = if (!empty(deviceSecurityGroupProperties)) { + name: 'deviceSecurityGroups' + properties: { + thresholdRules: deviceSecurityGroupProperties.thresholdRules + timeWindowRules: deviceSecurityGroupProperties.timeWindowRules + allowlistRules: deviceSecurityGroupProperties.allowlistRules + denylistRules: deviceSecurityGroupProperties.denylistRules + } +} + +module iotSecuritySolutions '.bicep/nested_iotSecuritySolutions.bicep' = if (!empty(ioTSecuritySolutionProperties)) { + name: '${uniqueString(deployment().name)}-ASC-IotSecuritySolutions' + scope: resourceGroup(empty(ioTSecuritySolutionProperties) ? 'dummy' : ioTSecuritySolutionProperties.resourceGroup) + params: { + ioTSecuritySolutionProperties: ioTSecuritySolutionProperties + } +} + +resource securityContacts 'Microsoft.Security/securityContacts@2017-08-01-preview' = if (!empty(securityContactProperties)) { + name: 'securityContacts' + properties: { + email: securityContactProperties.email + phone: securityContactProperties.phone + alertNotifications: securityContactProperties.alertNotifications + alertsToAdmins: securityContactProperties.alertsToAdmins + } +} + +resource workspaceSettings 'Microsoft.Security/workspaceSettings@2017-08-01-preview' = { + name: 'default' + properties: { + workspaceId: workspaceId + scope: scope + } + dependsOn: [ + autoProvisioningSettings + ] +} + +@description('The resource ID of the used log analytics workspace.') +output workspaceId string = workspaceId + +@description('The name of the security center.') +output name string = 'Security' diff --git a/modules/Microsoft.Security/azureSecurityCenter/readme.md b/modules/Microsoft.Security/azureSecurityCenter/readme.md new file mode 100644 index 0000000..ad45c55 --- /dev/null +++ b/modules/Microsoft.Security/azureSecurityCenter/readme.md @@ -0,0 +1,166 @@ +# Azure Security Center `[Microsoft.Security/azureSecurityCenter]` + +This template enables Azure security center - Standard tier by default, could be overridden. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Security/autoProvisioningSettings` | [2017-08-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2017-08-01-preview/autoProvisioningSettings) | +| `Microsoft.Security/deviceSecurityGroups` | [2019-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2019-08-01/deviceSecurityGroups) | +| `Microsoft.Security/iotSecuritySolutions` | [2019-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2019-08-01/iotSecuritySolutions) | +| `Microsoft.Security/pricings` | [2018-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2018-06-01/pricings) | +| `Microsoft.Security/securityContacts` | [2017-08-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2017-08-01-preview/securityContacts) | +| `Microsoft.Security/workspaceSettings` | [2017-08-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Security/2017-08-01-preview/workspaceSettings) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `scope` | string | All the VMs in this scope will send their security data to the mentioned workspace unless overridden by a setting with more specific scope. | +| `workspaceId` | string | The full Azure ID of the workspace to save the data in. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `appServicesPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for AppServices. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `armPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for ARM. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `autoProvision` | string | `'On'` | `[Off, On]` | Describes what kind of security agent provisioning action to take. - On or Off. | +| `containerRegistryPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for ContainerRegistry. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `containersTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for containers. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `cosmosDbsTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for CosmosDbs. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `deviceSecurityGroupProperties` | object | `{object}` | | Device Security group data. | +| `dnsPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for DNS. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ioTSecuritySolutionProperties` | object | `{object}` | | Security Solution data. | +| `keyVaultsPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for KeyVaults. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `kubernetesServicePricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for KubernetesService. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `openSourceRelationalDatabasesTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for OpenSourceRelationalDatabases. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `securityContactProperties` | object | `{object}` | | Security contact data. | +| `sqlServersPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for SqlServers. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `sqlServerVirtualMachinesPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for SqlServerVirtualMachines. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `storageAccountsPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for StorageAccounts. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | +| `virtualMachinesPricingTier` | string | `'Free'` | `[Free, Standard]` | The pricing tier value for VMs. Azure Security Center is provided in two pricing tiers: free and standard, with the standard tier available with a trial period. The standard tier offers advanced security capabilities, while the free tier offers basic security features. - Free or Standard. | + + +### Parameter Usage: `securityContactProperties` + +

+ +Parameter JSON format + +```json +"securityContactProperties": { + "value": { + "email": "test@contoso.com", + "phone": "+12345678", + "alertNotifications": "On", + "alertsToAdmins": "Off" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +securityContactProperties: { + email: 'test@contoso.com' + phone: '+12345678' + alertNotifications: 'On' + alertsToAdmins: 'Off' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the security center. | +| `workspaceId` | string | The resource ID of the used log analytics workspace. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module azureSecurityCenter './Microsoft.Security/azureSecurityCenter/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-AzureSecurityCenter' + params: { + // Required parameters + scope: '/subscriptions/<>' + workspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + // Non-required parameters + securityContactProperties: { + alertNotifications: 'Off' + alertsToAdmins: 'Off' + email: 'foo@contoso.com' + phone: '+12345678' + } + } +} +``` + +
+

+ +

+ +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 + "scope": { + "value": "/subscriptions/<>" + }, + "workspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + // Non-required parameters + "securityContactProperties": { + "value": { + "alertNotifications": "Off", + "alertsToAdmins": "Off", + "email": "foo@contoso.com", + "phone": "+12345678" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Security/azureSecurityCenter/version.json b/modules/Microsoft.Security/azureSecurityCenter/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Security/azureSecurityCenter/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ServiceBus/namespaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..8e95107 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Service Bus Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + 'Azure Service Bus Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0') + 'Azure Service Bus Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + '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 namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(namespace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: namespace +}] diff --git a/modules/Microsoft.ServiceBus/namespaces/.test/min.parameters.json b/modules/Microsoft.ServiceBus/namespaces/.test/min.parameters.json new file mode 100644 index 0000000..fb97a21 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/.test/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": "<>-az-sbn-min-001" + } + } +} diff --git a/modules/Microsoft.ServiceBus/namespaces/.test/parameters.json b/modules/Microsoft.ServiceBus/namespaces/.test/parameters.json new file mode 100644 index 0000000..20f9d06 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/.test/parameters.json @@ -0,0 +1,184 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sbn-x-002" + }, + "lock": { + "value": "CanNotDelete" + }, + "skuName": { + "value": "Premium" + }, + "tags": { + "value": {} + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "disasterRecoveryConfigs": { + "value": {} + }, + "migrationConfigurations": { + "value": {} + }, + "networkRuleSets": { + "value": { + "defaultAction": "Deny", + "trustedServiceAccessEnabled": true, + "virtualNetworkRules": [ + { + "ignoreMissingVnetServiceEndpoint": true, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-003" + } + ], + "ipRules": [ + { + "ipMask": "10.0.1.0/32", + "action": "Allow" + }, + { + "ipMask": "10.0.2.0/32", + "action": "Allow" + } + ] + } + }, + "authorizationRules": { + "value": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ] + }, + "queues": { + "value": [ + { + "name": "<>-az-sbq-x-002", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ] + } + ] + }, + "topics": { + "value": [ + { + "name": "<>-az-sbt-x-001", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ], + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "namespace", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + } + } + ] + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + } + } +} diff --git a/modules/Microsoft.ServiceBus/namespaces/.test/pe.parameters.json b/modules/Microsoft.ServiceBus/namespaces/.test/pe.parameters.json new file mode 100644 index 0000000..3a84849 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/.test/pe.parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sbn-pe-001" + }, + "skuName": { + "value": "Premium" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "namespace", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.ServiceBus/namespaces/authorizationRules/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/deploy.bicep new file mode 100644 index 0000000..bbb2ad7 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/deploy.bicep @@ -0,0 +1,51 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@description('Required. The name of the authorization rule.') +param name string + +@description('Optional. The rights associated with the rule.') +@allowed([ + 'Listen' + 'Manage' + 'Send' +]) +param rights array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource authorizationRule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2017-04-01' = { + name: name + parent: namespace + properties: { + rights: rights + } +} + +@description('The name of the authorization rule.') +output name string = authorizationRule.name + +@description('The resource ID of the authorization rule.') +output resourceId string = authorizationRule.id + +@description('The name of the Resource Group the authorization rule was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/authorizationRules/readme.md b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/readme.md new file mode 100644 index 0000000..c775cc2 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/readme.md @@ -0,0 +1,47 @@ +# ServiceBus Namespace Authorization Rules `[Microsoft.ServiceBus/namespaces/authorizationRules]` + +This module deploys authorization rules for a service bus namespace + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/AuthorizationRules` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/AuthorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `rights` | array | `[]` | `[Listen, Manage, Send]` | The rights associated with the rule. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | +| `resourceGroupName` | string | The name of the Resource Group the authorization rule was created in. | +| `resourceId` | string | The resource ID of the authorization rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/authorizationRules/version.json b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/authorizationRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/deploy.bicep new file mode 100644 index 0000000..2e8f290 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/deploy.bicep @@ -0,0 +1,378 @@ +@description('Required. Name of the Service Bus Namespace.') +@maxLength(50) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Name of this SKU. - Basic, Standard, Premium.') +@allowed([ + 'Basic' + 'Standard' + 'Premium' +]) +param skuName string = 'Basic' + +@description('Optional. Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones.') +param zoneRedundant bool = false + +@description('Optional. Authorization Rules for the Service Bus namespace.') +param authorizationRules array = [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } +] + +@description('Optional. The migration configuration.') +param migrationConfigurations object = {} + +@description('Optional. The disaster recovery configuration.') +param disasterRecoveryConfigs object = {} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@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. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Configure networking options for Premium SKU Service Bus. This object contains IPs/Subnets to allow or restrict access to private endpoints only. For security reasons, it is recommended to configure this object on the Namespace.') +param networkRuleSets object = {} + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The queues to create in the service bus namespace.') +param queues array = [] + +@description('Optional. The topics to create in the service bus namespace.') +param topics array = [] + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key.') +param cMKKeyName string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. Enable infrastructure encryption (double encryption). Note, this setting requires the configuration of Customer-Managed-Keys (CMK) via the corresponding module parameters.') +param requireInfrastructureEncryption bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'OperationalLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'OperationalLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2021-11-01' = { + name: name + location: location + tags: empty(tags) ? null : tags + sku: { + name: skuName + } + identity: identity + properties: { + zoneRedundant: zoneRedundant + encryption: !empty(cMKKeyName) ? { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: [ + { + identity: !empty(cMKUserAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } : null + keyName: cMKKeyName + keyVaultUri: cMKKeyVault.properties.vaultUri + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVaultKey.properties.keyUriWithVersion, '/')) + } + ] + requireInfrastructureEncryption: requireInfrastructureEncryption + } : null + } +} + +module serviceBusNamespace_authorizationRules 'authorizationRules/deploy.bicep' = [for (authorizationRule, index) in authorizationRules: { + name: '${uniqueString(deployment().name, location)}-AuthorizationRules-${index}' + params: { + namespaceName: serviceBusNamespace.name + name: authorizationRule.name + rights: contains(authorizationRule, 'rights') ? authorizationRule.rights : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module serviceBusNamespace_disasterRecoveryConfig 'disasterRecoveryConfigs/deploy.bicep' = if (!empty(disasterRecoveryConfigs)) { + name: '${uniqueString(deployment().name, location)}-DisasterRecoveryConfig' + params: { + namespaceName: serviceBusNamespace.name + name: contains(disasterRecoveryConfigs, 'name') ? disasterRecoveryConfigs.name : 'default' + alternateName: contains(disasterRecoveryConfigs, 'alternateName') ? disasterRecoveryConfigs.alternateName : '' + partnerNamespaceResourceID: contains(disasterRecoveryConfigs, 'partnerNamespace') ? disasterRecoveryConfigs.partnerNamespace : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module serviceBusNamespace_migrationConfigurations 'migrationConfigurations/deploy.bicep' = if (!empty(migrationConfigurations)) { + name: '${uniqueString(deployment().name, location)}-MigrationConfigurations' + params: { + namespaceName: serviceBusNamespace.name + name: contains(migrationConfigurations, 'name') ? migrationConfigurations.name : '$default' + postMigrationName: migrationConfigurations.postMigrationName + targetNamespaceResourceId: migrationConfigurations.targetNamespace + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module serviceBusNamespace_networkRuleSet 'networkRuleSets/deploy.bicep' = if (!empty(networkRuleSets) || !empty(privateEndpoints)) { + name: '${uniqueString(deployment().name, location)}-NetworkRuleSet' + params: { + namespaceName: serviceBusNamespace.name + publicNetworkAccess: contains(networkRuleSets, 'publicNetworkAccess') ? networkRuleSets.publicNetworkAccess : (!empty(privateEndpoints) && empty(networkRuleSets) ? 'Disabled' : 'Enabled') + defaultAction: contains(networkRuleSets, 'defaultAction') ? networkRuleSets.defaultAction : 'Allow' + trustedServiceAccessEnabled: contains(networkRuleSets, 'trustedServiceAccessEnabled') ? networkRuleSets.trustedServiceAccessEnabled : true + ipRules: contains(networkRuleSets, 'ipRules') ? networkRuleSets.ipRules : [] + virtualNetworkRules: contains(networkRuleSets, 'virtualNetworkRules') ? networkRuleSets.virtualNetworkRules : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module serviceBusNamespace_queues 'queues/deploy.bicep' = [for (queue, index) in queues: { + name: '${uniqueString(deployment().name, location)}-Queue-${index}' + params: { + namespaceName: serviceBusNamespace.name + name: queue.name + authorizationRules: contains(queue, 'authorizationRules') ? queue.authorizationRules : [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + ] + deadLetteringOnMessageExpiration: contains(queue, 'deadLetteringOnMessageExpiration') ? queue.deadLetteringOnMessageExpiration : true + defaultMessageTimeToLive: contains(queue, 'defaultMessageTimeToLive') ? queue.defaultMessageTimeToLive : 'P14D' + duplicateDetectionHistoryTimeWindow: contains(queue, 'duplicateDetectionHistoryTimeWindow') ? queue.duplicateDetectionHistoryTimeWindow : 'PT10M' + enableBatchedOperations: contains(queue, 'enableBatchedOperations') ? queue.enableBatchedOperations : true + enableExpress: contains(queue, 'enableExpress') ? queue.enableExpress : false + enablePartitioning: contains(queue, 'enablePartitioning') ? queue.enablePartitioning : false + lock: contains(queue, 'lock') ? queue.lock : '' + lockDuration: contains(queue, 'lockDuration') ? queue.lockDuration : 'PT1M' + maxDeliveryCount: contains(queue, 'maxDeliveryCount') ? queue.maxDeliveryCount : 10 + maxSizeInMegabytes: contains(queue, 'maxSizeInMegabytes') ? queue.maxSizeInMegabytes : 1024 + requiresDuplicateDetection: contains(queue, 'requiresDuplicateDetection') ? queue.requiresDuplicateDetection : false + requiresSession: contains(queue, 'requiresSession') ? queue.requiresSession : false + roleAssignments: contains(queue, 'roleAssignments') ? queue.roleAssignments : [] + status: contains(queue, 'status') ? queue.status : 'Active' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module serviceBusNamespace_topics 'topics/deploy.bicep' = [for (topic, index) in topics: { + name: '${uniqueString(deployment().name, location)}-Topic-${index}' + params: { + namespaceName: serviceBusNamespace.name + name: topic.name + authorizationRules: contains(topic, 'authorizationRules') ? topic.authorizationRules : [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + ] + autoDeleteOnIdle: contains(topic, 'autoDeleteOnIdle') ? topic.autoDeleteOnIdle : 'PT5M' + defaultMessageTimeToLive: contains(topic, 'defaultMessageTimeToLive') ? topic.defaultMessageTimeToLive : 'P14D' + duplicateDetectionHistoryTimeWindow: contains(topic, 'duplicateDetectionHistoryTimeWindow') ? topic.duplicateDetectionHistoryTimeWindow : 'PT10M' + enableBatchedOperations: contains(topic, 'enableBatchedOperations') ? topic.enableBatchedOperations : true + enableExpress: contains(topic, 'enableExpress') ? topic.enableExpress : false + enablePartitioning: contains(topic, 'enablePartitioning') ? topic.enablePartitioning : false + lock: contains(topic, 'lock') ? topic.lock : '' + maxMessageSizeInKilobytes: contains(topic, 'maxMessageSizeInKilobytes') ? topic.maxMessageSizeInKilobytes : 1024 + maxSizeInMegabytes: contains(topic, 'maxSizeInMegabytes') ? topic.maxSizeInMegabytes : 1024 + requiresDuplicateDetection: contains(topic, 'requiresDuplicateDetection') ? topic.requiresDuplicateDetection : false + roleAssignments: contains(topic, 'roleAssignments') ? topic.roleAssignments : [] + status: contains(topic, 'status') ? topic.status : 'Active' + supportOrdering: contains(topic, 'supportOrdering') ? topic.supportOrdering : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource serviceBusNamespace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${serviceBusNamespace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: serviceBusNamespace +} + +resource serviceBusNamespace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: serviceBusNamespace +} + +module serviceBusNamespace_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-Namespace-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(serviceBusNamespace.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: serviceBusNamespace.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module serviceBusNamespace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: serviceBusNamespace.id + } +}] + +@description('The resource ID of the deployed service bus namespace.') +output resourceId string = serviceBusNamespace.id + +@description('The resource group of the deployed service bus namespace.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed service bus namespace.') +output name string = serviceBusNamespace.name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(serviceBusNamespace.identity, 'principalId') ? serviceBusNamespace.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = serviceBusNamespace.location diff --git a/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/deploy.bicep new file mode 100644 index 0000000..28fb278 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/deploy.bicep @@ -0,0 +1,50 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@description('Optional. The name of the disaster recovery config.') +param name string = 'default' + +@description('Optional. Primary/Secondary eventhub namespace name, which is part of GEO DR pairing.') +param alternateName string = '' + +@description('Optional. Resource ID of the Primary/Secondary event hub namespace name, which is part of GEO DR pairing.') +param partnerNamespaceResourceID string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource disasterRecoveryConfig 'Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs@2017-04-01' = { + name: name + parent: namespace + properties: { + alternateName: alternateName + partnerNamespace: partnerNamespaceResourceID + } +} + +@description('The name of the disaster recovery config.') +output name string = disasterRecoveryConfig.name + +@description('The Resource ID of the disaster recovery config.') +output resourceId string = disasterRecoveryConfig.id + +@description('The name of the Resource Group the disaster recovery config was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/readme.md b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/readme.md new file mode 100644 index 0000000..9660476 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/readme.md @@ -0,0 +1,44 @@ +# Service Bus Namespace Disaster Recovery Config `[Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs]` + +This module deploys a disaster recovery config for a service bus Namespace + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/disasterRecoveryConfigs) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `alternateName` | string | `''` | Primary/Secondary eventhub namespace name, which is part of GEO DR pairing. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | The name of the disaster recovery config. | +| `partnerNamespaceResourceID` | string | `''` | Resource ID of the Primary/Secondary event hub namespace name, which is part of GEO DR pairing. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the disaster recovery config. | +| `resourceGroupName` | string | The name of the Resource Group the disaster recovery config was created in. | +| `resourceId` | string | The Resource ID of the disaster recovery config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/version.json b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/deploy.bicep new file mode 100644 index 0000000..3cc505b --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/deploy.bicep @@ -0,0 +1,50 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@description('Optional. The name of the migration configuration.') +param name string = '$default' + +@description('Required. Name to access Standard Namespace after migration.') +param postMigrationName string + +@description('Required. Existing premium Namespace resource ID which has no entities, will be used for migration.') +param targetNamespaceResourceId string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource migrationConfiguration 'Microsoft.ServiceBus/namespaces/migrationConfigurations@2017-04-01' = { + name: name + parent: namespace + properties: { + targetNamespace: targetNamespaceResourceId + postMigrationName: postMigrationName + } +} + +@description('The name of the migration configuration.') +output name string = migrationConfiguration.name + +@description('The Resource ID of the migration configuration.') +output resourceId string = migrationConfiguration.id + +@description('The name of the Resource Group the migration configuration was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/readme.md b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/readme.md new file mode 100644 index 0000000..60ebea3 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/readme.md @@ -0,0 +1,48 @@ +# ServiceBus Namespace Migration Configuration `[Microsoft.ServiceBus/namespaces/migrationConfigurations]` + +This module deploys a migration configuration for a service bus namespace + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/migrationConfigurations` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/migrationConfigurations) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `postMigrationName` | string | Name to access Standard Namespace after migration. | +| `targetNamespaceResourceId` | string | Existing premium Namespace resource ID which has no entities, will be used for migration. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'$default'` | The name of the migration configuration. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the migration configuration. | +| `resourceGroupName` | string | The name of the Resource Group the migration configuration was created in. | +| `resourceId` | string | The Resource ID of the migration configuration. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/version.json b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/migrationConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/deploy.bicep new file mode 100644 index 0000000..6be7153 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/deploy.bicep @@ -0,0 +1,78 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Network Rule Set. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. This determines if traffic is allowed over public network. Default is "Enabled". If set to "Disabled", traffic to this namespace will be restricted over Private Endpoints only and network rules will not be applied.') +param publicNetworkAccess string = 'Enabled' + +@allowed([ + 'Allow' + 'Deny' +]) +@description('Optional. Default Action for Network Rule Set. Default is "Allow". It will not be set if publicNetworkAccess is "Disabled". Otherwise, it will be set to "Deny" if ipRules or virtualNetworkRules are being used.') +param defaultAction string = 'Allow' + +@allowed([ + true + false +]) +@description('Optional. Value that indicates whether Trusted Service Access is enabled or not. Default is "true". It will not be set if publicNetworkAccess is "Disabled".') +param trustedServiceAccessEnabled bool = true + +@description('Optional. List virtual network rules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny".') +param virtualNetworkRules array = [] + +@description('Optional. List of IpRules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny".') +param ipRules array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var networkRules = [for (virtualNetworkRule, index) in virtualNetworkRules: { + ignoreMissingVnetServiceEndpoint: contains(virtualNetworkRule, 'ignoreMissingVnetServiceEndpoint') ? virtualNetworkRule.ignoreMissingVnetServiceEndpoint : null + subnet: contains(virtualNetworkRule, 'subnetResourceId') ? { + id: virtualNetworkRule.subnetResourceId + } : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource networkRuleSet 'Microsoft.ServiceBus/namespaces/networkRuleSets@2021-11-01' = { + name: 'default' + parent: namespace + properties: { + publicNetworkAccess: publicNetworkAccess + defaultAction: publicNetworkAccess == 'Disabled' ? null : (!empty(ipRules) || !empty(virtualNetworkRules) ? 'Deny' : defaultAction) + trustedServiceAccessEnabled: publicNetworkAccess == 'Disabled' ? null : trustedServiceAccessEnabled + ipRules: publicNetworkAccess == 'Disabled' ? null : ipRules + virtualNetworkRules: publicNetworkAccess == 'Disabled' ? null : networkRules + } +} + +@description('The name of the network rule set.') +output name string = networkRuleSet.name + +@description('The resource ID of the network rule set.') +output resourceId string = networkRuleSet.id + +@description('The name of the resource group the network rule set was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/readme.md b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/readme.md new file mode 100644 index 0000000..c04d228 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/readme.md @@ -0,0 +1,46 @@ +# ServiceBus Namespaces NetworkRuleSets `[Microsoft.ServiceBus/namespaces/networkRuleSets]` + +This module deploys ServiceBus Namespaces NetworkRuleSets. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/networkRuleSets` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-11-01/namespaces/networkRuleSets) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Network Rule Set. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `defaultAction` | string | `'Allow'` | `[Allow, Deny]` | Default Action for Network Rule Set. Default is "Allow". It will not be set if publicNetworkAccess is "Disabled". Otherwise, it will be set to "Deny" if ipRules or virtualNetworkRules are being used. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ipRules` | array | `[]` | | List of IpRules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny". | +| `publicNetworkAccess` | string | `'Enabled'` | `[Disabled, Enabled]` | This determines if traffic is allowed over public network. Default is "Enabled". If set to "Disabled", traffic to this namespace will be restricted over Private Endpoints only and network rules will not be applied. | +| `trustedServiceAccessEnabled` | bool | `True` | `[False, True]` | Value that indicates whether Trusted Service Access is enabled or not. Default is "true". It will not be set if publicNetworkAccess is "Disabled". | +| `virtualNetworkRules` | array | `[]` | | List virtual network rules. It will not be set if publicNetworkAccess is "Disabled". Otherwise, when used, defaultAction will be set to "Deny". | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the network rule set. | +| `resourceGroupName` | string | The name of the resource group the network rule set was created in. | +| `resourceId` | string | The resource ID of the network rule set. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/version.json b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/version.json new file mode 100644 index 0000000..09da9cf --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/networkRuleSets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.2" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ServiceBus/namespaces/queues/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d1eaf5e --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Service Bus Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + 'Azure Service Bus Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0') + 'Azure Service Bus Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + '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 queue 'Microsoft.ServiceBus/namespaces/queues@2021-06-01-preview' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssigment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(queue.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: queue +}] diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/deploy.bicep new file mode 100644 index 0000000..52bc0a9 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/deploy.bicep @@ -0,0 +1,56 @@ +@description('Required. The name of the service bus namepace queue.') +param name string + +@description('Conditional. The name of the parent Service Bus Namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Conditional. The name of the parent Service Bus Namespace Queue. Required if the template is used in a standalone deployment.') +param queueName string + +@description('Optional. The rights associated with the rule.') +@allowed([ + 'Listen' + 'Manage' + 'Send' +]) +param rights array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName + + resource queue 'queues@2021-06-01-preview' existing = { + name: queueName + } +} + +resource authorizationRule 'Microsoft.ServiceBus/namespaces/queues/authorizationRules@2017-04-01' = { + name: name + parent: namespace::queue + properties: { + rights: rights + } +} + +@description('The name of the authorization rule.') +output name string = authorizationRule.name + +@description('The Resource ID of the authorization rule.') +output resourceId string = authorizationRule.id + +@description('The name of the Resource Group the authorization rule was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/readme.md b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/readme.md new file mode 100644 index 0000000..1cf9885 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/readme.md @@ -0,0 +1,48 @@ +# ServiceBus Namespace Queue Authorization Rules `[Microsoft.ServiceBus/namespaces/queues/authorizationRules]` + +This module deploys an authorization rule for a service bus namespace queue. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/queues/authorizationRules` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/queues/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the service bus namepace queue. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace. Required if the template is used in a standalone deployment. | +| `queueName` | string | The name of the parent Service Bus Namespace Queue. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `rights` | array | `[]` | `[Listen, Manage, Send]` | The rights associated with the rule. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | +| `resourceGroupName` | string | The name of the Resource Group the authorization rule was created in. | +| `resourceId` | string | The Resource ID of the authorization rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/version.json b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/authorizationRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/queues/deploy.bicep new file mode 100644 index 0000000..f3dfd89 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/deploy.bicep @@ -0,0 +1,163 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@description('Required. Name of the Service Bus Queue.') +@minLength(6) +@maxLength(50) +param name string + +@description('Optional. ISO 8601 timespan duration of a peek-lock; that is, the amount of time that the message is locked for other receivers. The maximum value for LockDuration is 5 minutes; the default value is 1 minute.') +param lockDuration string = 'PT1M' + +@description('Optional. The maximum size of the queue in megabytes, which is the size of memory allocated for the queue. Default is 1024.') +param maxSizeInMegabytes int = 1024 + +@description('Optional. A value indicating if this queue requires duplicate detection.') +param requiresDuplicateDetection bool = false + +@description('Optional. A value that indicates whether the queue supports the concept of sessions.') +param requiresSession bool = false + +@description('Optional. ISO 8601 default message timespan to live value. This is the duration after which the message expires, starting from when the message is sent to Service Bus. This is the default value used when TimeToLive is not set on a message itself.') +param defaultMessageTimeToLive string = 'P14D' + +@description('Optional. A value that indicates whether this queue has dead letter support when a message expires.') +param deadLetteringOnMessageExpiration bool = true + +@description('Optional. Value that indicates whether server-side batched operations are enabled.') +param enableBatchedOperations bool = true + +@description('Optional. ISO 8601 timeSpan structure that defines the duration of the duplicate detection history. The default value is 10 minutes.') +param duplicateDetectionHistoryTimeWindow string = 'PT10M' + +@description('Optional. The maximum delivery count. A message is automatically deadlettered after this number of deliveries. default value is 10.') +param maxDeliveryCount int = 10 + +@description('Optional. Enumerates the possible values for the status of a messaging entity. - Active, Disabled, Restoring, SendDisabled, ReceiveDisabled, Creating, Deleting, Renaming, Unknown.') +@allowed([ + 'Active' + 'Disabled' + 'Restoring' + 'SendDisabled' + 'ReceiveDisabled' + 'Creating' + 'Deleting' + 'Renaming' + 'Unknown' +]) +param status string = 'Active' + +@description('Optional. A value that indicates whether the queue is to be partitioned across multiple message brokers.') +param enablePartitioning bool = false + +@description('Optional. A value that indicates whether Express Entities are enabled. An express queue holds a message in memory temporarily before writing it to persistent storage.') +param enableExpress bool = false + +@description('Optional. Authorization Rules for the Service Bus Queue.') +param authorizationRules array = [ + { + name: 'RootManageSharedAccessKey' + properties: { + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + } +] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource queue 'Microsoft.ServiceBus/namespaces/queues@2021-06-01-preview' = { + name: name + parent: namespace + properties: { + lockDuration: lockDuration + maxSizeInMegabytes: maxSizeInMegabytes + requiresDuplicateDetection: requiresDuplicateDetection + requiresSession: requiresSession + defaultMessageTimeToLive: defaultMessageTimeToLive + deadLetteringOnMessageExpiration: deadLetteringOnMessageExpiration + enableBatchedOperations: enableBatchedOperations + duplicateDetectionHistoryTimeWindow: duplicateDetectionHistoryTimeWindow + maxDeliveryCount: maxDeliveryCount + status: status + enablePartitioning: enablePartitioning + enableExpress: enableExpress + } +} + +module queue_authorizationRules 'authorizationRules/deploy.bicep' = [for (authorizationRule, index) in authorizationRules: { + name: '${deployment().name}-AuthRule-${index}' + params: { + namespaceName: namespaceName + queueName: queue.name + name: authorizationRule.name + rights: contains(authorizationRule, 'rights') ? authorizationRule.rights : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource queue_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${queue.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: queue +} + +module queue_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: queue.id + } +}] + +@description('The name of the deployed queue.') +output name string = queue.name + +@description('The resource ID of the deployed queue.') +output resourceId string = queue.id + +@description('The resource group of the deployed queue.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/readme.md b/modules/Microsoft.ServiceBus/namespaces/queues/readme.md new file mode 100644 index 0000000..8bc8b2a --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/readme.md @@ -0,0 +1,123 @@ +# ServiceBus Namespace Queue `[Microsoft.ServiceBus/namespaces/queues]` + +This module deploys a queue for a service bus namespace. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ServiceBus/namespaces/queues` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/queues) | +| `Microsoft.ServiceBus/namespaces/queues/authorizationRules` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/queues/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Service Bus Queue. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authorizationRules` | _[authorizationRules](authorizationRules/readme.md)_ array | `[System.Collections.Hashtable]` | | Authorization Rules for the Service Bus Queue. | +| `deadLetteringOnMessageExpiration` | bool | `True` | | A value that indicates whether this queue has dead letter support when a message expires. | +| `defaultMessageTimeToLive` | string | `'P14D'` | | ISO 8601 default message timespan to live value. This is the duration after which the message expires, starting from when the message is sent to Service Bus. This is the default value used when TimeToLive is not set on a message itself. | +| `duplicateDetectionHistoryTimeWindow` | string | `'PT10M'` | | ISO 8601 timeSpan structure that defines the duration of the duplicate detection history. The default value is 10 minutes. | +| `enableBatchedOperations` | bool | `True` | | Value that indicates whether server-side batched operations are enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableExpress` | bool | `False` | | A value that indicates whether Express Entities are enabled. An express queue holds a message in memory temporarily before writing it to persistent storage. | +| `enablePartitioning` | bool | `False` | | A value that indicates whether the queue is to be partitioned across multiple message brokers. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `lockDuration` | string | `'PT1M'` | | ISO 8601 timespan duration of a peek-lock; that is, the amount of time that the message is locked for other receivers. The maximum value for LockDuration is 5 minutes; the default value is 1 minute. | +| `maxDeliveryCount` | int | `10` | | The maximum delivery count. A message is automatically deadlettered after this number of deliveries. default value is 10. | +| `maxSizeInMegabytes` | int | `1024` | | The maximum size of the queue in megabytes, which is the size of memory allocated for the queue. Default is 1024. | +| `requiresDuplicateDetection` | bool | `False` | | A value indicating if this queue requires duplicate detection. | +| `requiresSession` | bool | `False` | | A value that indicates whether the queue supports the concept of sessions. | +| `roleAssignments` | array | `[]` | | 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'. | +| `status` | string | `'Active'` | `[Active, Creating, Deleting, Disabled, ReceiveDisabled, Renaming, Restoring, SendDisabled, Unknown]` | Enumerates the possible values for the status of a messaging entity. - Active, Disabled, Restoring, SendDisabled, ReceiveDisabled, Creating, Deleting, Renaming, Unknown. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed queue. | +| `resourceGroupName` | string | The resource group of the deployed queue. | +| `resourceId` | string | The resource ID of the deployed queue. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/queues/version.json b/modules/Microsoft.ServiceBus/namespaces/queues/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/queues/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/readme.md b/modules/Microsoft.ServiceBus/namespaces/readme.md new file mode 100644 index 0000000..53e599e --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/readme.md @@ -0,0 +1,801 @@ +# Service Bus Namespaces `[Microsoft.ServiceBus/namespaces]` + +This module deploys a service bus namespace resource. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.ServiceBus/namespaces` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-11-01/namespaces) | +| `Microsoft.ServiceBus/namespaces/AuthorizationRules` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/AuthorizationRules) | +| `Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/disasterRecoveryConfigs) | +| `Microsoft.ServiceBus/namespaces/migrationConfigurations` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/migrationConfigurations) | +| `Microsoft.ServiceBus/namespaces/networkRuleSets` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-11-01/namespaces/networkRuleSets) | +| `Microsoft.ServiceBus/namespaces/queues` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/queues) | +| `Microsoft.ServiceBus/namespaces/queues/authorizationRules` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2017-04-01/namespaces/queues/authorizationRules) | +| `Microsoft.ServiceBus/namespaces/topics` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/topics) | +| `Microsoft.ServiceBus/namespaces/topics/authorizationRules` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/topics/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Service Bus Namespace. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authorizationRules` | _[authorizationRules](authorizationRules/readme.md)_ array | `[System.Collections.Hashtable]` | | Authorization Rules for the Service Bus namespace. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `cMKUserAssignedIdentityResourceId` | string | `''` | | User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[OperationalLogs]` | `[OperationalLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disasterRecoveryConfigs` | _[disasterRecoveryConfigs](disasterRecoveryConfigs/readme.md)_ object | `{object}` | | The disaster recovery configuration. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `migrationConfigurations` | _[migrationConfigurations](migrationConfigurations/readme.md)_ object | `{object}` | | The migration configuration. | +| `networkRuleSets` | _[networkRuleSets](networkRuleSets/readme.md)_ object | `{object}` | | Configure networking options for Premium SKU Service Bus. This object contains IPs/Subnets to allow or restrict access to private endpoints only. For security reasons, it is recommended to configure this object on the Namespace. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `queues` | _[queues](queues/readme.md)_ array | `[]` | | The queues to create in the service bus namespace. | +| `requireInfrastructureEncryption` | bool | `True` | | Enable infrastructure encryption (double encryption). Note, this setting requires the configuration of Customer-Managed-Keys (CMK) via the corresponding module parameters. | +| `roleAssignments` | array | `[]` | | 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'. | +| `skuName` | string | `'Basic'` | `[Basic, Premium, Standard]` | Name of this SKU. - Basic, Standard, Premium. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `topics` | _[topics](topics/readme.md)_ array | `[]` | | The topics to create in the service bus namespace. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `zoneRedundant` | bool | `False` | | Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `networkAcl` + +Configure networing options on premium SKU only. + +

+ +Parameter JSON format + +```json +"networkAclConfig": { + "value" : { + "publicNetworkAccess": "Disabled", + "allowTrustedServices": true + } +} + + +``` + +
+ +
+ +Bicep format + +```bicep +networkingAclConfig: { + publicNetworkAccess: "Disabled" + allowTrustedServices: true +} + +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed service bus namespace. | +| `resourceGroupName` | string | The resource group of the deployed service bus namespace. | +| `resourceId` | string | The resource ID of the deployed service bus namespace. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.ServiceBus/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + name: '<>-az-sbn-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sbn-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.ServiceBus/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + // Required parameters + name: '<>-az-sbn-x-002' + // Non-required parameters + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'AnotherKey' + rights: [ + 'Listen' + 'Send' + ] + } + ] + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + disasterRecoveryConfigs: {} + lock: 'CanNotDelete' + migrationConfigurations: {} + networkRuleSets: { + defaultAction: 'Deny' + ipRules: [ + { + action: 'Allow' + ipMask: '10.0.1.0/32' + } + { + action: 'Allow' + ipMask: '10.0.2.0/32' + } + ] + trustedServiceAccessEnabled: true + virtualNetworkRules: [ + { + ignoreMissingVnetServiceEndpoint: true + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-003' + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net' + ] + } + service: 'namespace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + queues: [ + { + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'AnotherKey' + rights: [ + 'Listen' + 'Send' + ] + } + ] + name: '<>-az-sbq-x-002' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Premium' + systemAssignedIdentity: true + tags: {} + topics: [ + { + authorizationRules: [ + { + name: 'RootManageSharedAccessKey' + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + { + name: 'AnotherKey' + rights: [ + 'Listen' + 'Send' + ] + } + ] + name: '<>-az-sbt-x-001' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + ] + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-sbn-x-002" + }, + // Non-required parameters + "authorizationRules": { + "value": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ] + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "disasterRecoveryConfigs": { + "value": {} + }, + "lock": { + "value": "CanNotDelete" + }, + "migrationConfigurations": { + "value": {} + }, + "networkRuleSets": { + "value": { + "defaultAction": "Deny", + "ipRules": [ + { + "action": "Allow", + "ipMask": "10.0.1.0/32" + }, + { + "action": "Allow", + "ipMask": "10.0.2.0/32" + } + ], + "trustedServiceAccessEnabled": true, + "virtualNetworkRules": [ + { + "ignoreMissingVnetServiceEndpoint": true, + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-003" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + }, + "service": "namespace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "queues": { + "value": [ + { + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ], + "name": "<>-az-sbq-x-002", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuName": { + "value": "Premium" + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": {} + }, + "topics": { + "value": [ + { + "authorizationRules": [ + { + "name": "RootManageSharedAccessKey", + "rights": [ + "Listen", + "Manage", + "Send" + ] + }, + { + "name": "AnotherKey", + "rights": [ + "Listen", + "Send" + ] + } + ], + "name": "<>-az-sbt-x-001", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + ] + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module namespaces './Microsoft.ServiceBus/namespaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Namespaces' + params: { + // Required parameters + name: '<>-az-sbn-pe-001' + // Non-required parameters + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net' + ] + } + service: 'namespace' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + skuName: 'Premium' + } +} +``` + +
+

+ +

+ +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": "<>-az-sbn-pe-001" + }, + // Non-required parameters + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net" + ] + }, + "service": "namespace", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "skuName": { + "value": "Premium" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ServiceBus/namespaces/topics/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..2fdcf6f --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Azure Service Bus Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + 'Azure Service Bus Data Receiver': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0') + 'Azure Service Bus Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + '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 topic 'Microsoft.ServiceBus/namespaces/topics@2021-06-01-preview' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssigment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(topic.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: topic +}] diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/deploy.bicep new file mode 100644 index 0000000..4c9f8b3 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/deploy.bicep @@ -0,0 +1,56 @@ +@description('Required. The name of the service bus namespace topic.') +param name string + +@description('Conditional. The name of the parent Service Bus Namespace. Required if the template is used in a standalone deployment.') +param namespaceName string + +@description('Conditional. The name of the parent Service Bus Namespace Topic. Required if the template is used in a standalone deployment.') +param topicName string + +@description('Optional. The rights associated with the rule.') +@allowed([ + 'Listen' + 'Manage' + 'Send' +]) +param rights array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName + + resource topic 'topics@2021-06-01-preview' existing = { + name: topicName + } +} + +resource authorizationRule 'Microsoft.ServiceBus/namespaces/topics/authorizationRules@2021-06-01-preview' = { + name: name + parent: namespace::topic + properties: { + rights: rights + } +} + +@description('The name of the authorization rule.') +output name string = authorizationRule.name + +@description('The Resource ID of the authorization rule.') +output resourceId string = authorizationRule.id + +@description('The name of the Resource Group the authorization rule was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/readme.md b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/readme.md new file mode 100644 index 0000000..f14dec0 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/readme.md @@ -0,0 +1,48 @@ +# ServiceBus Namespace Topic Authorization Rules `[Microsoft.ServiceBus/namespaces/topics/authorizationRules]` + +This module deploys an authorization rule for a service bus namespace topic. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceBus/namespaces/topics/authorizationRules` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/topics/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the service bus namespace topic. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace. Required if the template is used in a standalone deployment. | +| `topicName` | string | The name of the parent Service Bus Namespace Topic. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `rights` | array | `[]` | `[Listen, Manage, Send]` | The rights associated with the rule. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the authorization rule. | +| `resourceGroupName` | string | The name of the Resource Group the authorization rule was created in. | +| `resourceId` | string | The Resource ID of the authorization rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/version.json b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/authorizationRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/deploy.bicep b/modules/Microsoft.ServiceBus/namespaces/topics/deploy.bicep new file mode 100644 index 0000000..eca1634 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/deploy.bicep @@ -0,0 +1,159 @@ +@description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Topic. Required if the template is used in a standalone deployment.') +@minLength(6) +@maxLength(50) +param namespaceName string + +@description('Required. Name of the Service Bus Topic.') +@minLength(6) +@maxLength(50) +param name string + +@description('Optional. The maximum size of the topic in megabytes, which is the size of memory allocated for the topic. Default is 1024.') +param maxSizeInMegabytes int = 1024 + +@description('Optional. A value indicating if this topic requires duplicate detection.') +param requiresDuplicateDetection bool = false + +@description('Optional. ISO 8601 default message timespan to live value. This is the duration after which the message expires, starting from when the message is sent to Service Bus. This is the default value used when TimeToLive is not set on a message itself.') +param defaultMessageTimeToLive string = 'P14D' + +@description('Optional. Value that indicates whether server-side batched operations are enabled.') +param enableBatchedOperations bool = true + +@description('Optional. ISO 8601 timeSpan structure that defines the duration of the duplicate detection history. The default value is 10 minutes.') +param duplicateDetectionHistoryTimeWindow string = 'PT10M' + +@description('Optional. Maximum size (in KB) of the message payload that can be accepted by the topic. This property is only used in Premium today and default is 1024.') +param maxMessageSizeInKilobytes int = 1024 + +@description('Optional. Value that indicates whether the topic supports ordering.') +param supportOrdering bool = false + +@description('Optional. ISO 8601 timespan idle interval after which the topic is automatically deleted. The minimum duration is 5 minutes.') +param autoDeleteOnIdle string = 'PT5M' + +@description('Optional. Enumerates the possible values for the status of a messaging entity. - Active, Disabled, Restoring, SendDisabled, ReceiveDisabled, Creating, Deleting, Renaming, Unknown.') +@allowed([ + 'Active' + 'Disabled' + 'Restoring' + 'SendDisabled' + 'ReceiveDisabled' + 'Creating' + 'Deleting' + 'Renaming' + 'Unknown' +]) +param status string = 'Active' + +@description('Optional. A value that indicates whether the topic is to be partitioned across multiple message brokers.') +param enablePartitioning bool = false + +@description('Optional. A value that indicates whether Express Entities are enabled. An express topic holds a message in memory temporarily before writing it to persistent storage.') +param enableExpress bool = false + +@description('Optional. Authorization Rules for the Service Bus Topic.') +param authorizationRules array = [ + { + name: 'RootManageSharedAccessKey' + properties: { + rights: [ + 'Listen' + 'Manage' + 'Send' + ] + } + } +] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = { + name: namespaceName +} + +resource topic 'Microsoft.ServiceBus/namespaces/topics@2021-06-01-preview' = { + name: name + parent: namespace + properties: { + autoDeleteOnIdle: autoDeleteOnIdle + defaultMessageTimeToLive: defaultMessageTimeToLive + duplicateDetectionHistoryTimeWindow: duplicateDetectionHistoryTimeWindow + enableBatchedOperations: enableBatchedOperations + enableExpress: enableExpress + enablePartitioning: enablePartitioning + maxMessageSizeInKilobytes: maxMessageSizeInKilobytes + maxSizeInMegabytes: maxSizeInMegabytes + requiresDuplicateDetection: requiresDuplicateDetection + status: status + supportOrdering: supportOrdering + } +} + +module topic_authorizationRules 'authorizationRules/deploy.bicep' = [for (authorizationRule, index) in authorizationRules: { + name: '${deployment().name}-AuthRule-${index}' + params: { + namespaceName: namespaceName + topicName: topic.name + name: authorizationRule.name + rights: contains(authorizationRule, 'rights') ? authorizationRule.rights : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource topic_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${topic.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: topic +} + +module topic_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: topic.id + } +}] + +@description('The name of the deployed topic.') +output name string = topic.name + +@description('The resource ID of the deployed topic.') +output resourceId string = topic.id + +@description('The resource group of the deployed topic.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/readme.md b/modules/Microsoft.ServiceBus/namespaces/topics/readme.md new file mode 100644 index 0000000..ac2dc54 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/readme.md @@ -0,0 +1,122 @@ +# ServiceBus Namespace Topic `[Microsoft.ServiceBus/namespaces/topics]` + +This module deploys a topic for a service bus namespace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ServiceBus/namespaces/topics` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/topics) | +| `Microsoft.ServiceBus/namespaces/topics/authorizationRules` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2021-06-01-preview/namespaces/topics/authorizationRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Service Bus Topic. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `namespaceName` | string | The name of the parent Service Bus Namespace for the Service Bus Topic. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authorizationRules` | _[authorizationRules](authorizationRules/readme.md)_ array | `[System.Collections.Hashtable]` | | Authorization Rules for the Service Bus Topic. | +| `autoDeleteOnIdle` | string | `'PT5M'` | | ISO 8601 timespan idle interval after which the topic is automatically deleted. The minimum duration is 5 minutes. | +| `defaultMessageTimeToLive` | string | `'P14D'` | | ISO 8601 default message timespan to live value. This is the duration after which the message expires, starting from when the message is sent to Service Bus. This is the default value used when TimeToLive is not set on a message itself. | +| `duplicateDetectionHistoryTimeWindow` | string | `'PT10M'` | | ISO 8601 timeSpan structure that defines the duration of the duplicate detection history. The default value is 10 minutes. | +| `enableBatchedOperations` | bool | `True` | | Value that indicates whether server-side batched operations are enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableExpress` | bool | `False` | | A value that indicates whether Express Entities are enabled. An express topic holds a message in memory temporarily before writing it to persistent storage. | +| `enablePartitioning` | bool | `False` | | A value that indicates whether the topic is to be partitioned across multiple message brokers. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxMessageSizeInKilobytes` | int | `1024` | | Maximum size (in KB) of the message payload that can be accepted by the topic. This property is only used in Premium today and default is 1024. | +| `maxSizeInMegabytes` | int | `1024` | | The maximum size of the topic in megabytes, which is the size of memory allocated for the topic. Default is 1024. | +| `requiresDuplicateDetection` | bool | `False` | | A value indicating if this topic requires duplicate detection. | +| `roleAssignments` | array | `[]` | | 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'. | +| `status` | string | `'Active'` | `[Active, Creating, Deleting, Disabled, ReceiveDisabled, Renaming, Restoring, SendDisabled, Unknown]` | Enumerates the possible values for the status of a messaging entity. - Active, Disabled, Restoring, SendDisabled, ReceiveDisabled, Creating, Deleting, Renaming, Unknown. | +| `supportOrdering` | bool | `False` | | Value that indicates whether the topic supports ordering. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed topic. | +| `resourceGroupName` | string | The resource group of the deployed topic. | +| `resourceId` | string | The resource ID of the deployed topic. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceBus/namespaces/topics/version.json b/modules/Microsoft.ServiceBus/namespaces/topics/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/topics/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceBus/namespaces/version.json b/modules/Microsoft.ServiceBus/namespaces/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.ServiceBus/namespaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.ServiceFabric/clusters/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.ServiceFabric/clusters/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..2af9f06 --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(serviceFabricCluster.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: serviceFabricCluster +}] diff --git a/modules/Microsoft.ServiceFabric/clusters/.test/cert.parameters.json b/modules/Microsoft.ServiceFabric/clusters/.test/cert.parameters.json new file mode 100644 index 0000000..668b6d8 --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/.test/cert.parameters.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sfc-cert-001" + }, + "managementEndpoint": { + "value": "https://<>-az-sfc-cert-001.westeurope.cloudapp.azure.com:19080" + }, + "reliabilityLevel": { + "value": "None" + }, + "certificate": { + "value": { + "thumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", // Mutual exclusive with the other cert specs + "x509StoreName": "My" + } + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + } + } +} diff --git a/modules/Microsoft.ServiceFabric/clusters/.test/full.parameters.json b/modules/Microsoft.ServiceFabric/clusters/.test/full.parameters.json new file mode 100644 index 0000000..46c19ee --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/.test/full.parameters.json @@ -0,0 +1,208 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sfc-full-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "tags": { + "value": { + "resourceType": "Service Fabric", + "clusterName": "<>-az-sfc-full-001" + } + }, + "addOnFeatures": { + "value": [ + "RepairManager", + "DnsService", + "BackupRestoreService", + "ResourceMonitorService" + ] + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "<>", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "<>" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "https://adp<>azsaweux001.blob.core.windows.net/", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "https://adp<>azsaweux001.queue.core.windows.net/", + "storageAccountName": "adp<>azsaweux001", + "tableEndpoint": "https://adp<>azsaweux001.table.core.windows.net/" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "managementEndpoint": { + "value": "https://<>-az-sfc-full-001.westeurope.cloudapp.azure.com:19080" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "capacities": {}, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "startPort": 49000 + }, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "vmInstanceCount": 5 + } + ] + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "upgradeDescription": { + "value": { + "forceRestart": false, + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "healthCheckWaitDuration": "00:00:30", + "healthCheckStableDuration": "00:01:00", + "healthCheckRetryTimeout": "00:45:00", + "upgradeTimeout": "02:00:00", + "upgradeDomainTimeout": "02:00:00", + "healthPolicy": { + "maxPercentUnhealthyNodes": 0, + "maxPercentUnhealthyApplications": 0 + }, + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0, + "maxPercentDeltaUnhealthyApplications": 0 + } + } + }, + "reliabilityLevel": { + "value": "Silver" + }, + "vmImage": { + "value": "Linux" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" // not idempotent + } + ] + } + } +} diff --git a/modules/Microsoft.ServiceFabric/clusters/.test/min.parameters.json b/modules/Microsoft.ServiceFabric/clusters/.test/min.parameters.json new file mode 100644 index 0000000..b7673a7 --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/.test/min.parameters.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sfc-min-001" + }, + "managementEndpoint": { + "value": "https://<>-az-sfc-min-001.westeurope.cloudapp.azure.com:19080" + }, + "reliabilityLevel": { + "value": "None" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + } + } +} diff --git a/modules/Microsoft.ServiceFabric/clusters/applicationTypes/deploy.bicep b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/deploy.bicep new file mode 100644 index 0000000..f74dc4b --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/deploy.bicep @@ -0,0 +1,42 @@ +@description('Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment.') +param serviceFabricClusterName string = '' + +@description('Optional. Application type name.') +param name string = 'defaultApplicationType' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' existing = { + name: serviceFabricClusterName +} + +resource applicationTypes 'Microsoft.ServiceFabric/clusters/applicationTypes@2021-06-01' = { + name: name + parent: serviceFabricCluster + tags: tags +} + +@description('The resource name of the Application type.') +output name string = applicationTypes.name + +@description('The resource group of the Application type.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Application type.') +output resourceID string = applicationTypes.id diff --git a/modules/Microsoft.ServiceFabric/clusters/applicationTypes/readme.md b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/readme.md new file mode 100644 index 0000000..ea3c5ed --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/readme.md @@ -0,0 +1,84 @@ +# Service Fabric Cluster Application Types `[Microsoft.ServiceFabric/clusters/applicationTypes]` + +This module deploys a Service Fabric Cluster Application Type. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `serviceFabricClusterName` | string | `''` | The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'defaultApplicationType'` | Application type name. | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The resource name of the Application type. | +| `resourceGroupName` | string | The resource group of the Application type. | +| `resourceID` | string | The resource ID of the Application type. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.ServiceFabric/clusters/applicationTypes/version.json b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/applicationTypes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.ServiceFabric/clusters/deploy.bicep b/modules/Microsoft.ServiceFabric/clusters/deploy.bicep new file mode 100644 index 0000000..48773b0 --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/deploy.bicep @@ -0,0 +1,330 @@ +@description('Required. Name of the Service Fabric cluster.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@allowed([ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' +]) +@description('Optional. The list of add-on features to enable in the cluster.') +param addOnFeatures array = [] + +@description('Optional. Number of unused versions per application type to keep.') +param maxUnusedVersionsToKeep int = 3 + +@description('Optional. The settings to enable AAD authentication on the cluster.') +param azureActiveDirectory object = {} + +@description('Optional. Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location.') +param certificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param certificateCommonNames object = {} + +@description('Optional. The list of client certificates referenced by common name that are allowed to manage the cluster.') +param clientCertificateCommonNames array = [] + +@description('Optional. The list of client certificates referenced by thumbprint that are allowed to manage the cluster.') +param clientCertificateThumbprints array = [] + +@description('Optional. The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions.') +param clusterCodeVersion string = '' + +@description('Optional. The storage account information for storing Service Fabric diagnostic logs.') +param diagnosticsStorageAccountConfig object = {} + +@description('Optional. Indicates if the event store service is enabled.') +param eventStoreServiceEnabled bool = false + +@description('Optional. The list of custom fabric settings to configure the cluster.') +param fabricSettings array = [] + +@description('Optional. Indicates if infrastructure service manager is enabled.') +param infrastructureServiceManager bool = false + +@description('Required. The http management endpoint of the cluster.') +param managementEndpoint string + +@description('Optional. The list of node types in the cluster.') +param nodeTypes array = [] + +@description('Optional. Indicates a list of notification channels for cluster events.') +param notifications array = [] + +@allowed([ + 'Bronze' + 'Gold' + 'None' + 'Platinum' + 'Silver' +]) +@description('Optional. The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9.') +param reliabilityLevel string + +@description('Optional. Describes the certificate details.') +param reverseProxyCertificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param reverseProxyCertificateCommonNames object = {} + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster.') +param sfZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Describes the policy used when upgrading the cluster.') +param upgradeDescription object = {} + +@allowed([ + 'Automatic' + 'Manual' +]) +@description('Optional. The upgrade mode of the cluster when new Service Fabric runtime version is available.') +param upgradeMode string = 'Automatic' + +@description('Optional. Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseEndTimestampUtc string = '' + +@description('Optional. Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseStartTimestampUtc string = '' + +@allowed([ + 'Wave0' + 'Wave1' + 'Wave2' +]) +@description('Optional. Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0.') +param upgradeWave string = 'Wave0' + +@description('Optional. The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used.') +param vmImage string = '' + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added.') +param vmssZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Boolean to pause automatic runtime version upgrades to the cluster.') +param waveUpgradePaused bool = false + +@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. Array of Service Fabric cluster application types.') +param applicationTypes array = [] + +var enableReferencedModulesTelemetry = false + +var clientCertificateCommonNames_var = [for clientCertificateCommonName in clientCertificateCommonNames: { + certificateCommonName: contains(clientCertificateCommonName, 'certificateCommonName') ? clientCertificateCommonName.certificateCommonName : null + certificateIssuerThumbprint: contains(clientCertificateCommonName, 'certificateIssuerThumbprint') ? clientCertificateCommonName.certificateIssuerThumbprint : null + isAdmin: contains(clientCertificateCommonName, 'isAdmin') ? clientCertificateCommonName.isAdmin : false +}] + +var clientCertificateThumbprints_var = [for clientCertificateThumbprint in clientCertificateThumbprints: { + certificateThumbprint: contains(clientCertificateThumbprint, 'certificateThumbprint') ? clientCertificateThumbprint.certificateThumbprint : null + isAdmin: contains(clientCertificateThumbprint, 'isAdmin') ? clientCertificateThumbprint.isAdmin : false +}] + +var fabricSettings_var = [for fabricSetting in fabricSettings: { + name: contains(fabricSetting, 'name') ? fabricSetting.name : null + parameters: contains(fabricSetting, 'parameters') ? fabricSetting.parameters : null +}] + +var nodeTypes_var = [for nodeType in nodeTypes: { + applicationPorts: contains(nodeType, 'applicationPorts') ? { + endPort: contains(nodeType.applicationPorts, 'endPort') ? nodeType.applicationPorts.endPort : null + startPort: contains(nodeType.applicationPorts, 'startPort') ? nodeType.applicationPorts.startPort : null + } : null + capacities: contains(nodeType, 'capacities') ? nodeType.capacities : null + clientConnectionEndpointPort: contains(nodeType, 'clientConnectionEndpointPort') ? nodeType.clientConnectionEndpointPort : null + durabilityLevel: contains(nodeType, 'durabilityLevel') ? nodeType.durabilityLevel : null + ephemeralPorts: contains(nodeType, 'ephemeralPorts') ? { + endPort: contains(nodeType.ephemeralPorts, 'endPort') ? nodeType.ephemeralPorts.endPort : null + startPort: contains(nodeType.ephemeralPorts, 'startPort') ? nodeType.ephemeralPorts.startPort : null + } : null + httpGatewayEndpointPort: contains(nodeType, 'httpGatewayEndpointPort') ? nodeType.httpGatewayEndpointPort : null + isPrimary: contains(nodeType, 'isPrimary') ? nodeType.isPrimary : null + isStateless: contains(nodeType, 'isStateless') ? nodeType.isStateless : null + multipleAvailabilityZones: contains(nodeType, 'multipleAvailabilityZones') ? nodeType.multipleAvailabilityZones : null + name: contains(nodeType, 'name') ? nodeType.name : 'Node00' + placementProperties: contains(nodeType, 'placementProperties') ? nodeType.placementProperties : null + reverseProxyEndpointPort: contains(nodeType, 'reverseProxyEndpointPort') ? nodeType.reverseProxyEndpointPort : null + vmInstanceCount: contains(nodeType, 'vmInstanceCount') ? nodeType.vmInstanceCount : 1 +}] + +var notifications_var = [for notification in notifications: { + isEnabled: contains(notification, 'isEnabled') ? notification.isEnabled : false + notificationCategory: contains(notification, 'notificationCategory') ? notification.notificationCategory : 'WaveProgress' + notificationLevel: contains(notification, 'notificationLevel') ? notification.notificationLevel : 'All' + notificationTargets: contains(notification, 'notificationTargets') ? notification.notificationTargets : [] +}] + +var upgradeDescription_var = union({ + deltaHealthPolicy: { + applicationDeltaHealthPolicies: contains(upgradeDescription, 'applicationDeltaHealthPolicies') ? upgradeDescription.applicationDeltaHealthPolicies : {} + maxPercentDeltaUnhealthyApplications: contains(upgradeDescription, 'maxPercentDeltaUnhealthyApplications') ? upgradeDescription.maxPercentDeltaUnhealthyApplications : 0 + maxPercentDeltaUnhealthyNodes: contains(upgradeDescription, 'maxPercentDeltaUnhealthyNodes') ? upgradeDescription.maxPercentDeltaUnhealthyNodes : 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: contains(upgradeDescription, 'maxPercentUpgradeDomainDeltaUnhealthyNodes') ? upgradeDescription.maxPercentUpgradeDomainDeltaUnhealthyNodes : 0 + } + forceRestart: contains(upgradeDescription, 'forceRestart') ? upgradeDescription.forceRestart : false + healthCheckRetryTimeout: contains(upgradeDescription, 'healthCheckRetryTimeout') ? upgradeDescription.healthCheckRetryTimeout : '00:45:00' + healthCheckStableDuration: contains(upgradeDescription, 'healthCheckStableDuration') ? upgradeDescription.healthCheckStableDuration : '00:01:00' + healthCheckWaitDuration: contains(upgradeDescription, 'healthCheckWaitDuration') ? upgradeDescription.healthCheckWaitDuration : '00:00:30' + upgradeDomainTimeout: contains(upgradeDescription, 'upgradeDomainTimeout') ? upgradeDescription.upgradeDomainTimeout : '02:00:00' + upgradeReplicaSetCheckTimeout: contains(upgradeDescription, 'upgradeReplicaSetCheckTimeout') ? upgradeDescription.upgradeReplicaSetCheckTimeout : '1.00:00:00' + upgradeTimeout: contains(upgradeDescription, 'upgradeTimeout') ? upgradeDescription.upgradeTimeout : '02:00:00' + }, contains(upgradeDescription, 'healthPolicy') ? { + healthPolicy: { + applicationHealthPolicies: contains(upgradeDescription.healthPolicy, 'applicationHealthPolicies') ? upgradeDescription.healthPolicy.applicationHealthPolicies : {} + maxPercentUnhealthyApplications: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyApplications') ? upgradeDescription.healthPolicy.maxPercentUnhealthyApplications : 0 + maxPercentUnhealthyNodes: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyNodes') ? upgradeDescription.healthPolicy.maxPercentUnhealthyNodes : 0 + } + } : {}) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// Service Fabric cluster resource +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' = { + name: name + location: location + tags: tags + properties: { + addOnFeatures: addOnFeatures + applicationTypeVersionsCleanupPolicy: { + maxUnusedVersionsToKeep: maxUnusedVersionsToKeep + } + azureActiveDirectory: !empty(azureActiveDirectory) ? { + clientApplication: contains(azureActiveDirectory, 'clientApplication') ? azureActiveDirectory.clientApplication : null + clusterApplication: contains(azureActiveDirectory, 'clusterApplication') ? azureActiveDirectory.clusterApplication : null + tenantId: contains(azureActiveDirectory, 'tenantId') ? azureActiveDirectory.tenantId : null + } : null + certificate: !empty(certificate) ? { + thumbprint: contains(certificate, 'thumbprint') ? certificate.thumbprint : null + thumbprintSecondary: contains(certificate, 'thumbprintSecondary') ? certificate.thumbprintSecondary : null + x509StoreName: contains(certificate, 'x509StoreName') ? certificate.x509StoreName : null + } : null + certificateCommonNames: !empty(certificateCommonNames) ? { + commonNames: contains(certificateCommonNames, 'commonNames') ? certificateCommonNames.commonNames : null + x509StoreName: contains(certificateCommonNames, 'certificateCommonNamesx509StoreName') ? certificateCommonNames.certificateCommonNamesx509StoreName : null + } : null + clientCertificateCommonNames: !empty(clientCertificateCommonNames) ? clientCertificateCommonNames_var : null + clientCertificateThumbprints: !empty(clientCertificateThumbprints) ? clientCertificateThumbprints_var : null + clusterCodeVersion: !empty(clusterCodeVersion) ? clusterCodeVersion : null + diagnosticsStorageAccountConfig: !empty(diagnosticsStorageAccountConfig) ? { + blobEndpoint: contains(diagnosticsStorageAccountConfig, 'blobEndpoint') ? diagnosticsStorageAccountConfig.blobEndpoint : null + protectedAccountKeyName: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName') ? diagnosticsStorageAccountConfig.protectedAccountKeyName : null + protectedAccountKeyName2: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName2') ? diagnosticsStorageAccountConfig.protectedAccountKeyName2 : null + queueEndpoint: contains(diagnosticsStorageAccountConfig, 'queueEndpoint') ? diagnosticsStorageAccountConfig.queueEndpoint : null + storageAccountName: contains(diagnosticsStorageAccountConfig, 'storageAccountName') ? diagnosticsStorageAccountConfig.storageAccountName : null + tableEndpoint: contains(diagnosticsStorageAccountConfig, 'tableEndpoint') ? diagnosticsStorageAccountConfig.tableEndpoint : null + } : null + eventStoreServiceEnabled: eventStoreServiceEnabled + fabricSettings: !empty(fabricSettings) ? fabricSettings_var : null + infrastructureServiceManager: infrastructureServiceManager + managementEndpoint: managementEndpoint + nodeTypes: !empty(nodeTypes) ? nodeTypes_var : [] + notifications: !empty(notifications) ? notifications_var : null + reliabilityLevel: !empty(reliabilityLevel) ? reliabilityLevel : 'None' + reverseProxyCertificate: !empty(reverseProxyCertificate) ? { + thumbprint: contains(reverseProxyCertificate, 'thumbprint') ? reverseProxyCertificate.thumbprint : null + thumbprintSecondary: contains(reverseProxyCertificate, 'thumbprintSecondary') ? reverseProxyCertificate.thumbprintSecondary : null + x509StoreName: contains(reverseProxyCertificate, 'x509StoreName') ? reverseProxyCertificate.x509StoreName : null + } : null + reverseProxyCertificateCommonNames: !empty(reverseProxyCertificateCommonNames) ? { + commonNames: contains(reverseProxyCertificateCommonNames, 'commonNames') ? reverseProxyCertificateCommonNames.commonNames : null + x509StoreName: contains(reverseProxyCertificateCommonNames, 'x509StoreName') ? reverseProxyCertificateCommonNames.x509StoreName : null + } : null + sfZonalUpgradeMode: !empty(sfZonalUpgradeMode) ? sfZonalUpgradeMode : null + upgradeDescription: !empty(upgradeDescription) ? upgradeDescription_var : null + upgradeMode: !empty(upgradeMode) ? upgradeMode : null + upgradePauseEndTimestampUtc: !empty(upgradePauseEndTimestampUtc) ? upgradePauseEndTimestampUtc : null + upgradePauseStartTimestampUtc: !empty(upgradePauseStartTimestampUtc) ? upgradePauseStartTimestampUtc : null + upgradeWave: !empty(upgradeWave) ? upgradeWave : null + vmImage: !empty(vmImage) ? vmImage : null + vmssZonalUpgradeMode: !empty(vmssZonalUpgradeMode) ? vmssZonalUpgradeMode : null + waveUpgradePaused: waveUpgradePaused + } +} + +// Service Fabric cluster resource lock +resource serviceFabricCluster_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${serviceFabricCluster.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: serviceFabricCluster +} + +// Service Fabric cluster RBAC assignment +module serviceFabricCluster_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ServiceFabric-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: serviceFabricCluster.id + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + } +}] + +// Service Fabric cluster application types +module serviceFabricCluster_applicationTypes 'applicationTypes/deploy.bicep' = [for applicationType in applicationTypes: { + name: '${uniqueString(deployment().name, location)}-SFC-${applicationType.name}' + params: { + name: applicationType.name + serviceFabricClusterName: serviceFabricCluster.name + tags: contains(applicationType, 'tags') ? applicationType.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The Service Fabric Cluster name.') +output name string = serviceFabricCluster.name + +@description('The Service Fabric Cluster resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The Service Fabric Cluster resource ID.') +output resourceId string = serviceFabricCluster.id + +@description('The Service Fabric Cluster endpoint.') +output endpoint string = serviceFabricCluster.properties.clusterEndpoint + +@description('The location the resource was deployed into.') +output location string = serviceFabricCluster.location diff --git a/modules/Microsoft.ServiceFabric/clusters/readme.md b/modules/Microsoft.ServiceFabric/clusters/readme.md new file mode 100644 index 0000000..c5df17e --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/readme.md @@ -0,0 +1,822 @@ +# Service Fabric Clusters `[Microsoft.ServiceFabric/clusters]` + +This module deploys a Service Fabric Cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ServiceFabric/clusters` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters) | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managementEndpoint` | string | The http management endpoint of the cluster. | +| `name` | string | Name of the Service Fabric cluster. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `addOnFeatures` | array | `[]` | `[BackupRestoreService, DnsService, RepairManager, ResourceMonitorService]` | The list of add-on features to enable in the cluster. | +| `applicationTypes` | _[applicationTypes](applicationTypes/readme.md)_ array | `[]` | | Array of Service Fabric cluster application types. | +| `azureActiveDirectory` | object | `{object}` | | The settings to enable AAD authentication on the cluster. | +| `certificate` | object | `{object}` | | Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location. | +| `certificateCommonNames` | object | `{object}` | | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| `clientCertificateCommonNames` | array | `[]` | | The list of client certificates referenced by common name that are allowed to manage the cluster. | +| `clientCertificateThumbprints` | array | `[]` | | The list of client certificates referenced by thumbprint that are allowed to manage the cluster. | +| `clusterCodeVersion` | string | `''` | | The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions. | +| `diagnosticsStorageAccountConfig` | object | `{object}` | | The storage account information for storing Service Fabric diagnostic logs. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `eventStoreServiceEnabled` | bool | `False` | | Indicates if the event store service is enabled. | +| `fabricSettings` | array | `[]` | | The list of custom fabric settings to configure the cluster. | +| `infrastructureServiceManager` | bool | `False` | | Indicates if infrastructure service manager is enabled. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxUnusedVersionsToKeep` | int | `3` | | Number of unused versions per application type to keep. | +| `nodeTypes` | array | `[]` | | The list of node types in the cluster. | +| `notifications` | array | `[]` | | Indicates a list of notification channels for cluster events. | +| `reliabilityLevel` | string | | `[Bronze, Gold, None, Platinum, Silver]` | The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9. | +| `reverseProxyCertificate` | object | `{object}` | | Describes the certificate details. | +| `reverseProxyCertificateCommonNames` | object | `{object}` | | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sfZonalUpgradeMode` | string | `'Hierarchical'` | `[Hierarchical, Parallel]` | This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `upgradeDescription` | object | `{object}` | | Describes the policy used when upgrading the cluster. | +| `upgradeMode` | string | `'Automatic'` | `[Automatic, Manual]` | The upgrade mode of the cluster when new Service Fabric runtime version is available. | +| `upgradePauseEndTimestampUtc` | string | `''` | | Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| `upgradePauseStartTimestampUtc` | string | `''` | | Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| `upgradeWave` | string | `'Wave0'` | `[Wave0, Wave1, Wave2]` | Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0. | +| `vmImage` | string | `''` | | The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used. | +| `vmssZonalUpgradeMode` | string | `'Hierarchical'` | `[Hierarchical, Parallel]` | This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added. | +| `waveUpgradePaused` | bool | `False` | | Boolean to pause automatic runtime version upgrades to the cluster. | + + +### Parameter Usage: `notifications` + +

+ +Parameter JSON format + +```json +"notifications": { + "value": [ + { + "isEnabled": true, // Required. Indicates if the notification is enabled. + "notificationCategory": "WaveProgress", // Required. The category of notification. Possible values include: "WaveProgress". + "notificationLevel": "Critical", // Required. The level of notification. Possible values include: "Critical", "All". + "notificationTargets": [ + { + "notificationChannel": "EmailUser", // Required. The notification channel indicates the type of receivers subscribed to the notification, either user or subscription. Possible values include: "EmailUser", "EmailSubscription". + "receivers": [ + "SomeReceiver" // Required. List of targets that subscribe to the notification. + ] + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +notifications: [ + { + isEnabled: true // Required. Indicates if the notification is enabled. + notificationCategory: 'WaveProgress' // Required. The category of notification. Possible values include: 'WaveProgress'. + notificationLevel: 'Critical' // Required. The level of notification. Possible values include: 'Critical' 'All'. + notificationTargets: [ + { + notificationChannel: 'EmailUser' // Required. The notification channel indicates the type of receivers subscribed to the notification either user or subscription. Possible values include: 'EmailUser' 'EmailSubscription'. + receivers: [ + 'SomeReceiver' // Required. List of targets that subscribe to the notification. + ] + } + ] + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The Service Fabric Cluster endpoint. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Service Fabric Cluster name. | +| `resourceGroupName` | string | The Service Fabric Cluster resource group. | +| `resourceId` | string | The Service Fabric Cluster resource ID. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Cert

+ +
+ +via Bicep module + +```bicep +module clusters './Microsoft.ServiceFabric/clusters/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Clusters' + params: { + // Required parameters + managementEndpoint: 'https://<>-az-sfc-cert-001.westeurope.cloudapp.azure.com:19080' + name: '<>-az-sfc-cert-001' + reliabilityLevel: 'None' + // Non-required parameters + certificate: { + thumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + x509StoreName: 'My' + } + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } +} +``` + +
+

+ +

+ +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 + "managementEndpoint": { + "value": "https://<>-az-sfc-cert-001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "<>-az-sfc-cert-001" + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "certificate": { + "value": { + "thumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "x509StoreName": "My" + } + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Full

+ +
+ +via Bicep module + +```bicep +module clusters './Microsoft.ServiceFabric/clusters/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Clusters' + params: { + // Required parameters + managementEndpoint: 'https://<>-az-sfc-full-001.westeurope.cloudapp.azure.com:19080' + name: '<>-az-sfc-full-001' + reliabilityLevel: 'Silver' + // Non-required parameters + addOnFeatures: [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + azureActiveDirectory: { + clientApplication: '<>' + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: '<>' + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: 'https://adp<>azsaweux001.blob.core.windows.net/' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: 'https://adp<>azsaweux001.queue.core.windows.net/' + storageAccountName: 'adp<>azsaweux001' + tableEndpoint: 'https://adp<>azsaweux001.table.core.windows.net/' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + lock: 'CanNotDelete' + maxUnusedVersionsToKeep: 2 + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + capacities: {} + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + isStateless: false + multipleAvailabilityZones: false + name: 'Node01' + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + startPort: 49000 + } + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + vmInstanceCount: 5 + } + ] + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + clusterName: '<>-az-sfc-full-001' + resourceType: 'Service Fabric' + } + upgradeDescription: { + deltaHealthPolicy: { + maxPercentDeltaUnhealthyApplications: 0 + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + } + forceRestart: false + healthCheckRetryTimeout: '00:45:00' + healthCheckStableDuration: '00:01:00' + healthCheckWaitDuration: '00:00:30' + healthPolicy: { + maxPercentUnhealthyApplications: 0 + maxPercentUnhealthyNodes: 0 + } + upgradeDomainTimeout: '02:00:00' + upgradeReplicaSetCheckTimeout: '1.00:00:00' + upgradeTimeout: '02:00:00' + } + vmImage: 'Linux' + } +} +``` + +
+

+ +

+ +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 + "managementEndpoint": { + "value": "https://<>-az-sfc-full-001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "<>-az-sfc-full-001" + }, + "reliabilityLevel": { + "value": "Silver" + }, + // Non-required parameters + "addOnFeatures": { + "value": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" + } + ] + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "<>", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "<>" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "https://adp<>azsaweux001.blob.core.windows.net/", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "https://adp<>azsaweux001.queue.core.windows.net/", + "storageAccountName": "adp<>azsaweux001", + "tableEndpoint": "https://adp<>azsaweux001.table.core.windows.net/" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "capacities": {}, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "startPort": 49000 + }, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "vmInstanceCount": 5 + } + ] + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "clusterName": "<>-az-sfc-full-001", + "resourceType": "Service Fabric" + } + }, + "upgradeDescription": { + "value": { + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyApplications": 0, + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0 + }, + "forceRestart": false, + "healthCheckRetryTimeout": "00:45:00", + "healthCheckStableDuration": "00:01:00", + "healthCheckWaitDuration": "00:00:30", + "healthPolicy": { + "maxPercentUnhealthyApplications": 0, + "maxPercentUnhealthyNodes": 0 + }, + "upgradeDomainTimeout": "02:00:00", + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "upgradeTimeout": "02:00:00" + } + }, + "vmImage": { + "value": "Linux" + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module clusters './Microsoft.ServiceFabric/clusters/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Clusters' + params: { + // Required parameters + managementEndpoint: 'https://<>-az-sfc-min-001.westeurope.cloudapp.azure.com:19080' + name: '<>-az-sfc-min-001' + reliabilityLevel: 'None' + // Non-required parameters + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } +} +``` + +
+

+ +

+ +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 + "managementEndpoint": { + "value": "https://<>-az-sfc-min-001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "<>-az-sfc-min-001" + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.ServiceFabric/clusters/version.json b/modules/Microsoft.ServiceFabric/clusters/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.ServiceFabric/clusters/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep b/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep new file mode 100644 index 0000000..b15faf6 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'SignalR AccessKey Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Web PubSub Service Owner (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12cf5a90-567b-43ae-8102-96cf46c7d9b4') + 'Web PubSub Service Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf') +} + +resource webPubSub 'Microsoft.SignalRService/webPubSub@2021-10-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(webPubSub.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: webPubSub +}] diff --git a/modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json b/modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json new file mode 100644 index 0000000..26f8284 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.test/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": "<>-az-pubsub-min-001" + } + } +} diff --git a/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json b/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json new file mode 100644 index 0000000..65c1b96 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "westeurope" + }, + "name": { + "value": "<>-az-pubsub-x-001" + }, + "capacity": { + "value": 2 + }, + "clientCertEnabled": { + "value": false + }, + "disableAadAuth": { + "value": false + }, + "disableLocalAuth": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "Standard_S1" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "privateEndpoints": [ + { + "name": "pe-<>-az-pubsub-x-001-webpubsub-0", + "allow": [], + "deny": [ + "ServerConnection", + "Trace" + ] + } + ], + "publicNetwork": { + "allow": [], + "deny": [ + "RESTAPI", + "Trace" + ] + } + } + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "purpose": "test" + } + }, + "resourceLogConfigurationsToEnable": { + "value": [ + "ConnectivityLogs" + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "webpubsub", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.SignalRService/webPubSub/.test/pe.parameters.json b/modules/Microsoft.SignalRService/webPubSub/.test/pe.parameters.json new file mode 100644 index 0000000..2f5e08b --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.test/pe.parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pubsub-pe-001" + }, + "sku": { + "value": "Standard_S1" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "webpubsub", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.SignalRService/webPubSub/deploy.bicep b/modules/Microsoft.SignalRService/webPubSub/deploy.bicep new file mode 100644 index 0000000..f150769 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/deploy.bicep @@ -0,0 +1,184 @@ +@description('Optional. The location for the resource.') +param location string = resourceGroup().location + +@description('Required. The name of the Web PubSub Service resource.') +param name string + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. The unit count of the resource. 1 by default.') +param capacity int = 1 + +@allowed([ + 'Free_F1' + 'Standard_S1' +]) +@description('Optional. Pricing tier of the resource.') +param sku string = 'Standard_S1' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. When set as true, connection with AuthType=aad won\'t work.') +param disableAadAuth bool = false + +@description('Optional. Disables all authentication methods other than AAD authentication. For security reasons, this value should be set to `true`.') +param disableLocalAuth bool = true + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@allowed([ + 'ConnectivityLogs' + 'MessagingLogs' +]) +@description('Optional. Control permission for data plane traffic coming from public networks while private endpoint is enabled.') +param resourceLogConfigurationsToEnable array = [ + 'ConnectivityLogs' + 'MessagingLogs' +] + +@description('Optional. Request client certificate during TLS handshake if enabled.') +param clientCertEnabled bool = false + +@description('Optional. Networks ACLs, this value contains IPs to allow and/or Subnet information. Can only be set if the \'SKU\' is not \'Free_F1\'. For security reasons, it is recommended to set the DefaultAction Deny.') +param networkAcls object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var resourceLogConfiguration = [for configuration in resourceLogConfigurationsToEnable: { + name: configuration + enabled: 'true' +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource webPubSub 'Microsoft.SignalRService/webPubSub@2021-10-01' = { + name: name + location: location + tags: tags + sku: { + capacity: capacity + name: sku + tier: sku == 'Standard_S1' ? 'Standard' : 'Free' + } + identity: identity + properties: { + disableAadAuth: disableAadAuth + disableLocalAuth: disableLocalAuth + networkACLs: !empty(networkAcls) ? any(networkAcls) : null + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + resourceLogConfiguration: { + categories: resourceLogConfiguration + } + tls: { + clientCertEnabled: clientCertEnabled + } + } +} + +module webPubSub_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-WebPubSub-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(webPubSub.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: webPubSub.id + subnetResourceId: privateEndpoint.subnetResourceId + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +resource webPubSub_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${webPubSub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: webPubSub +} + +module webPubSub_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-WebPubSub-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: webPubSub.id + } +}] + +@description('The Web PubSub name.') +output name string = webPubSub.name + +@description('The Web PubSub resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The Web PubSub resource ID.') +output resourceId string = webPubSub.id + +@description('The Web PubSub externalIP.') +output externalIP string = webPubSub.properties.externalIP + +@description('The Web PubSub hostName.') +output hostName string = webPubSub.properties.hostName + +@description('The Web PubSub publicPort.') +output publicPort int = webPubSub.properties.publicPort + +@description('The Web PubSub serverPort.') +output serverPort int = webPubSub.properties.serverPort + +@description('The location the resource was deployed into.') +output location string = webPubSub.location diff --git a/modules/Microsoft.SignalRService/webPubSub/readme.md b/modules/Microsoft.SignalRService/webPubSub/readme.md new file mode 100644 index 0000000..bb7d84a --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/readme.md @@ -0,0 +1,641 @@ +# Web PubSub Services `[Microsoft.SignalRService/webPubSub]` + +This module deploys a Web PubSub Service resource. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.SignalRService/webPubSub` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.SignalRService/2021-10-01/webPubSub) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Web PubSub Service resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `capacity` | int | `1` | | The unit count of the resource. 1 by default. | +| `clientCertEnabled` | bool | `False` | | Request client certificate during TLS handshake if enabled. | +| `disableAadAuth` | bool | `False` | | When set as true, connection with AuthType=aad won't work. | +| `disableLocalAuth` | bool | `True` | | Disables all authentication methods other than AAD authentication. For security reasons, this value should be set to `true`. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | The location for the resource. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkAcls` | object | `{object}` | | Networks ACLs, this value contains IPs to allow and/or Subnet information. Can only be set if the 'SKU' is not 'Free_F1'. For security reasons, it is recommended to set the DefaultAction Deny. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `resourceLogConfigurationsToEnable` | array | `[ConnectivityLogs, MessagingLogs]` | `[ConnectivityLogs, MessagingLogs]` | Control permission for data plane traffic coming from public networks while private endpoint is enabled. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'Standard_S1'` | `[Free_F1, Standard_S1]` | Pricing tier of the resource. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `networkAcls` + +Using this object you can configure the service's firewall. Note, that the `defaultAction` either allows all / denies all communication via the `publicNetwork` and `privateEndpoints`. You can subsequently allow/deny individual actions using the corresponding arrays. + +Either block supports any array of values: +- 'ClientConnection' +- 'RESTAPI' +- 'ServerConnection' +- 'Trace' + +

+ +Parameter JSON format + +```json +"networkAcls": { + "value": { + "defaultAction": "Deny", + "privateEndpoints": [ + { + "name": "pe-<>-az-pubsub-x-001-webpubsub-0", + "allow": [ + "ServerConnection", + "Trace" + ], + "deny": [] + } + ], + "publicNetwork": { + "allow": [ + "RESTAPI", + "Trace" + ], + "deny": [] + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +networkAcls: { + defaultAction: 'Deny' + privateEndpoints: [ + { + name: 'pe-<>-az-pubsub-x-001-webpubsub-0' + allow: [ + 'ServerConnection' + 'Trace' + ], + deny: [] + } + ] + publicNetwork: { + allow: [ + 'RESTAPI' + 'Trace' + ] + deny: [] + } +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `externalIP` | string | The Web PubSub externalIP. | +| `hostName` | string | The Web PubSub hostName. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Web PubSub name. | +| `publicPort` | int | The Web PubSub publicPort. | +| `resourceGroupName` | string | The Web PubSub resource group. | +| `resourceId` | string | The Web PubSub resource ID. | +| `serverPort` | int | The Web PubSub serverPort. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module webPubSub './Microsoft.SignalRService/webPubSub/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-WebPubSub' + params: { + name: '<>-az-pubsub-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pubsub-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module webPubSub './Microsoft.SignalRService/webPubSub/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-WebPubSub' + params: { + // Required parameters + name: '<>-az-pubsub-x-001' + // Non-required parameters + capacity: 2 + clientCertEnabled: false + disableAadAuth: false + disableLocalAuth: true + location: 'westeurope' + lock: 'CanNotDelete' + networkAcls: { + defaultAction: 'Allow' + privateEndpoints: [ + { + allow: [] + deny: [ + 'ServerConnection' + 'Trace' + ] + name: 'pe-<>-az-pubsub-x-001-webpubsub-0' + } + ] + publicNetwork: { + allow: [] + deny: [ + 'RESTAPI' + 'Trace' + ] + } + } + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com' + ] + } + service: 'webpubsub' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + resourceLogConfigurationsToEnable: [ + 'ConnectivityLogs' + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'Standard_S1' + systemAssignedIdentity: true + tags: { + purpose: '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": "<>-az-pubsub-x-001" + }, + // Non-required parameters + "capacity": { + "value": 2 + }, + "clientCertEnabled": { + "value": false + }, + "disableAadAuth": { + "value": false + }, + "disableLocalAuth": { + "value": true + }, + "location": { + "value": "westeurope" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "privateEndpoints": [ + { + "allow": [], + "deny": [ + "ServerConnection", + "Trace" + ], + "name": "pe-<>-az-pubsub-x-001-webpubsub-0" + } + ], + "publicNetwork": { + "allow": [], + "deny": [ + "RESTAPI", + "Trace" + ] + } + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com" + ] + }, + "service": "webpubsub", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "resourceLogConfigurationsToEnable": { + "value": [ + "ConnectivityLogs" + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "Standard_S1" + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "purpose": "test" + } + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +module webPubSub './Microsoft.SignalRService/webPubSub/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-WebPubSub' + params: { + // Required parameters + name: '<>-az-pubsub-pe-001' + // Non-required parameters + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com' + ] + } + service: 'webpubsub' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + sku: 'Standard_S1' + } +} +``` + +
+

+ +

+ +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": "<>-az-pubsub-pe-001" + }, + // Non-required parameters + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.webpubsub.azure.com" + ] + }, + "service": "webpubsub", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "sku": { + "value": "Standard_S1" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.SignalRService/webPubSub/version.json b/modules/Microsoft.SignalRService/webPubSub/version.json new file mode 100644 index 0000000..d52c7d0 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.6" +} diff --git a/modules/Microsoft.Sql/managedInstances/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Sql/managedInstances/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..ca54ad5 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,71 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(managedInstance.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: managedInstance +}] diff --git a/modules/Microsoft.Sql/managedInstances/.test/min.parameters.json b/modules/Microsoft.Sql/managedInstances/.test/min.parameters.json new file mode 100644 index 0000000..04f1025 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/.test/min.parameters.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sqlmi-min-001" + }, + "administratorLogin": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLogin" + } + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "subnetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi" + } + } +} diff --git a/modules/Microsoft.Sql/managedInstances/.test/parameters.json b/modules/Microsoft.Sql/managedInstances/.test/parameters.json new file mode 100644 index 0000000..3537d42 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/.test/parameters.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sqlmi-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "administratorLogin": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLogin" + } + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "subnetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "vCores": { + "value": 4 + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "hardwareFamily": { + "value": "Gen5" + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "dnsZonePartner": { + "value": "" + }, + "timezoneId": { + "value": "UTC" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "proxyOverride": { + "value": "Proxy" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "primaryUserAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "keys": { + "value": [ + { + "name": "adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07", // ID must be updated for new keys + "uri": "https://adp-<>-az-kv-x-sqlmi.vault.azure.net/keys/keyEncryptionKeySqlMi/4bf367f64c914d8ba698700fb598ad07", // ID must be updated for new keys + "serverKeyType": "AzureKeyVault" + } + ] + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07", // ID must be updated for new keys + "serverKeyType": "AzureKeyVault" + } + }, + "securityAlertPoliciesObj": { + "value": { + "name": "default", + "state": "Enabled", + "emailAccountAdmins": true + } + }, + "vulnerabilityAssessmentsObj": { + "value": { + "name": "default", + "emailSubscriptionAdmins": true, + "recurringScansIsEnabled": true, + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "vulnerabilityAssessmentsStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + }, + "databases": { + "value": [ + { + "name": "<>-az-sqlmidb-x-001", + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "backupLongTermRetentionPolicies": { + "name": "default" + } + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Sql/managedInstances/administrators/deploy.bicep b/modules/Microsoft.Sql/managedInstances/administrators/deploy.bicep new file mode 100644 index 0000000..afd232a --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/administrators/deploy.bicep @@ -0,0 +1,53 @@ +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. Login name of the managed instance administrator.') +param login string + +@description('Required. SID (object ID) of the managed instance administrator.') +param sid string + +@description('Optional. The name of the managed instance administrator.') +param name string = 'ActiveDirectory' + +@description('Optional. Tenant ID of the managed instance administrator.') +param tenantId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource administrator 'Microsoft.Sql/managedInstances/administrators@2022-02-01-preview' = { + name: name + parent: managedInstance + properties: { + administratorType: 'ActiveDirectory' + login: login + sid: sid + tenantId: tenantId + } +} + +@description('The name of the deployed managed instance administrator.') +output name string = administrator.name + +@description('The resource ID of the deployed managed instance administrator.') +output resourceId string = administrator.id + +@description('The resource group of the deployed managed instance administrator.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/administrators/readme.md b/modules/Microsoft.Sql/managedInstances/administrators/readme.md new file mode 100644 index 0000000..b078d56 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/administrators/readme.md @@ -0,0 +1,49 @@ +# SQL Managed Instances Administrator `[Microsoft.Sql/managedInstances/administrators]` + +This module deploys an administrator for the SQL managed instance + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/administrators` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/administrators) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `login` | string | Login name of the managed instance administrator. | +| `sid` | string | SID (object ID) of the managed instance administrator. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'ActiveDirectory'` | The name of the managed instance administrator. | +| `tenantId` | string | `''` | Tenant ID of the managed instance administrator. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance administrator. | +| `resourceGroupName` | string | The resource group of the deployed managed instance administrator. | +| `resourceId` | string | The resource ID of the deployed managed instance administrator. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/administrators/version.json b/modules/Microsoft.Sql/managedInstances/administrators/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/administrators/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/deploy.bicep b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/deploy.bicep new file mode 100644 index 0000000..1f6fdbb --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/deploy.bicep @@ -0,0 +1,63 @@ +@description('Required. The name of the Long Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The week of year to take the yearly backup in an ISO 8601 format.') +param weekOfYear int = 5 + +@description('Optional. The weekly retention policy for an LTR backup in an ISO 8601 format.') +param weeklyRetention string = 'P1M' + +@description('Optional. The monthly retention policy for an LTR backup in an ISO 8601 format.') +param monthlyRetention string = 'P1Y' + +@description('Optional. The yearly retention policy for an LTR backup in an ISO 8601 format.') +param yearlyRetention string = 'P5Y' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2020-02-02-preview' existing = { + name: databaseName + } +} + +resource backupLongTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies@2022-02-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + monthlyRetention: monthlyRetention + weeklyRetention: weeklyRetention + weekOfYear: weekOfYear + yearlyRetention: yearlyRetention + } +} + +@description('The name of the deployed database backup long-term retention policy.') +output name string = backupLongTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup long-term retention policy.') +output resourceId string = backupLongTermRetentionPolicy.id + +@description('The resource group of the deployed database backup long-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/readme.md b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/readme.md new file mode 100644 index 0000000..e8504dc --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/readme.md @@ -0,0 +1,51 @@ +# SQL Managed Instance Database Backup Long-Term Retention Policy `[Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies]` + +This module deploys a backup long-term retention policies for SQL Managed Instance databases + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Long Term Retention backup policy. For example "default". | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseName` | string | The name of the parent managed instance database. Required if the template is used in a standalone deployment. | +| `managedInstanceName` | string | The name of the parent managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `monthlyRetention` | string | `'P1Y'` | The monthly retention policy for an LTR backup in an ISO 8601 format. | +| `weeklyRetention` | string | `'P1M'` | The weekly retention policy for an LTR backup in an ISO 8601 format. | +| `weekOfYear` | int | `5` | The week of year to take the yearly backup in an ISO 8601 format. | +| `yearlyRetention` | string | `'P5Y'` | The yearly retention policy for an LTR backup in an ISO 8601 format. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup long-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup long-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup long-term retention policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/version.json b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/deploy.bicep b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/deploy.bicep new file mode 100644 index 0000000..a54bddd --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/deploy.bicep @@ -0,0 +1,51 @@ +@description('Required. The name of the Short Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported.') +param retentionDays int = 35 + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2020-02-02-preview' existing = { + name: databaseName + } +} + +resource backupShortTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies@2022-02-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + retentionDays: retentionDays + } +} + +@description('The name of the deployed database backup short-term retention policy.') +output name string = backupShortTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup short-term retention policy.') +output resourceId string = backupShortTermRetentionPolicy.id + +@description('The resource group of the deployed database backup short-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/readme.md b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/readme.md new file mode 100644 index 0000000..cc9fa72 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/readme.md @@ -0,0 +1,49 @@ +# SQL Managed Instance Database Backup Short-Term Retention Policy `[Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies]` + +This module deploys a backup short-term retention policies for SQL Managed Instance databases + + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupShortTermRetentionPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Short Term Retention backup policy. For example "default". | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `databaseName` | string | The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment. | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `retentionDays` | int | `35` | The backup retention period in days. This is how many days Point-in-Time Restore will be supported. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup short-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup short-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup short-term retention policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/version.json b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/databases/deploy.bicep b/modules/Microsoft.Sql/managedInstances/databases/deploy.bicep new file mode 100644 index 0000000..144a039 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/deploy.bicep @@ -0,0 +1,203 @@ +@description('Required. The name of the SQL managed instance database.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Collation of the managed instance database.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Collation of the managed instance.') +param catalogCollation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required).') +@allowed([ + 'Default' + 'RestoreExternalBackup' + 'PointInTimeRestore' + 'Recovery' + 'RestoreLongTermRetentionBackup' +]) +param createMode string = 'Default' + +@description('Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore.') +param sourceDatabaseId string = '' + +@description('Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore.') +param restorePointInTime string = '' + +@description('Optional. The restorable dropped database resource ID to restore when creating this database.') +param restorableDroppedDatabaseId string = '' + +@description('Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup.') +param storageContainerUri string = '' + +@description('Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup.') +param storageContainerSasToken string = '' + +@description('Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery.') +param recoverableDatabaseId string = '' + +@description('Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup.') +param longTermRetentionBackupResourceId string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. The configuration for the backup short term retention policy definition.') +param backupShortTermRetentionPoliciesObj object = {} + +@description('Optional. The configuration for the backup long term retention policy definition.') +param backupLongTermRetentionPoliciesObj object = {} + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'SQLInsights' + 'QueryStoreRuntimeStatistics' + 'QueryStoreWaitStatistics' + 'Errors' +]) +param diagnosticLogCategoriesToEnable array = [ + 'SQLInsights' + 'QueryStoreRuntimeStatistics' + 'QueryStoreWaitStatistics' + 'Errors' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource database 'Microsoft.Sql/managedInstances/databases@2022-02-01-preview' = { + name: name + parent: managedInstance + location: location + tags: tags + properties: { + collation: empty(collation) ? null : collation + restorePointInTime: empty(restorePointInTime) ? null : restorePointInTime + catalogCollation: empty(catalogCollation) ? null : catalogCollation + createMode: empty(createMode) ? null : createMode + storageContainerUri: empty(storageContainerUri) ? null : storageContainerUri + sourceDatabaseId: empty(sourceDatabaseId) ? null : sourceDatabaseId + restorableDroppedDatabaseId: empty(restorableDroppedDatabaseId) ? null : restorableDroppedDatabaseId + storageContainerSasToken: empty(storageContainerSasToken) ? null : storageContainerSasToken + recoverableDatabaseId: empty(recoverableDatabaseId) ? null : recoverableDatabaseId + longTermRetentionBackupResourceId: empty(longTermRetentionBackupResourceId) ? null : longTermRetentionBackupResourceId + } +} + +resource database_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${last(split(database.name, '/'))}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: database +} + +resource database_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: database +} + +module database_backupShortTermRetentionPolicy 'backupShortTermRetentionPolicies/deploy.bicep' = if (!empty(backupShortTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupShortTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/')) + name: backupShortTermRetentionPoliciesObj.name + retentionDays: contains(backupShortTermRetentionPoliciesObj, 'retentionDays') ? backupShortTermRetentionPoliciesObj.retentionDays : 35 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module database_backupLongTermRetentionPolicy 'backupLongTermRetentionPolicies/deploy.bicep' = if (!empty(backupLongTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupLongTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/')) + name: backupLongTermRetentionPoliciesObj.name + weekOfYear: contains(backupLongTermRetentionPoliciesObj, 'weekOfYear') ? backupLongTermRetentionPoliciesObj.weekOfYear : 5 + weeklyRetention: contains(backupLongTermRetentionPoliciesObj, 'weeklyRetention') ? backupLongTermRetentionPoliciesObj.weeklyRetention : 'P1M' + monthlyRetention: contains(backupLongTermRetentionPoliciesObj, 'monthlyRetention') ? backupLongTermRetentionPoliciesObj.monthlyRetention : 'P1Y' + yearlyRetention: contains(backupLongTermRetentionPoliciesObj, 'yearlyRetention') ? backupLongTermRetentionPoliciesObj.yearlyRetention : 'P5Y' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the deployed database.') +output name string = database.name + +@description('The resource ID of the deployed database.') +output resourceId string = database.id + +@description('The resource group the database was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = database.location diff --git a/modules/Microsoft.Sql/managedInstances/databases/readme.md b/modules/Microsoft.Sql/managedInstances/databases/readme.md new file mode 100644 index 0000000..4446785 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/readme.md @@ -0,0 +1,118 @@ +# SQL Managed Instances Database `[Microsoft.Sql/managedInstances/databases]` + +This template deploys a SQL Managed Instances Database. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances/databases` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupShortTermRetentionPolicies) | + +### Deployment prerequisites + +The SQL Managed Instance Database is deployed on a SQL Managed Instance. + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SQL managed instance database. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `longTermRetentionBackupResourceId` | string | `''` | The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup. | +| `managedInstanceName` | string | | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | +| `recoverableDatabaseId` | string | `''` | The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery. | +| `restorePointInTime` | string | `''` | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore. | +| `sourceDatabaseId` | string | `''` | The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore. | +| `storageContainerSasToken` | string | `''` | Specifies the storage container sas token. Required if createMode is RestoreExternalBackup. | +| `storageContainerUri` | string | `''` | Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backupLongTermRetentionPoliciesObj` | _[backupLongTermRetentionPolicies](backupLongTermRetentionPolicies/readme.md)_ object | `{object}` | | The configuration for the backup long term retention policy definition. | +| `backupShortTermRetentionPoliciesObj` | _[backupShortTermRetentionPolicies](backupShortTermRetentionPolicies/readme.md)_ object | `{object}` | | The configuration for the backup short term retention policy definition. | +| `catalogCollation` | string | `'SQL_Latin1_General_CP1_CI_AS'` | | Collation of the managed instance. | +| `collation` | string | `'SQL_Latin1_General_CP1_CI_AS'` | | Collation of the managed instance database. | +| `createMode` | string | `'Default'` | `[Default, PointInTimeRestore, Recovery, RestoreExternalBackup, RestoreLongTermRetentionBackup]` | Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required). | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[Errors, QueryStoreRuntimeStatistics, QueryStoreWaitStatistics, SQLInsights]` | `[Errors, QueryStoreRuntimeStatistics, QueryStoreWaitStatistics, SQLInsights]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `restorableDroppedDatabaseId` | string | `''` | | The restorable dropped database resource ID to restore when creating this database. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed database. | +| `resourceGroupName` | string | The resource group the database was deployed into. | +| `resourceId` | string | The resource ID of the deployed database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/databases/version.json b/modules/Microsoft.Sql/managedInstances/databases/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/databases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/deploy.bicep b/modules/Microsoft.Sql/managedInstances/deploy.bicep new file mode 100644 index 0000000..44ce08f --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/deploy.bicep @@ -0,0 +1,389 @@ +@description('Required. The name of the SQL managed instance.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The username used to establish jumpbox VMs.') +param administratorLogin string + +@description('Required. The password given to the admin user.') +@secure() +param administratorLoginPassword string + +@description('Required. The fully qualified resource ID of the subnet on which the SQL managed instance will be placed.') +param subnetId string + +@description('Optional. The name of the SKU, typically, a letter + Number code, e.g. P3.') +param skuName string = 'GP_Gen5' + +@description('Optional. The tier or edition of the particular SKU, e.g. Basic, Premium.') +param skuTier string = 'GeneralPurpose' + +@description('Optional. Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only.') +param storageSizeInGB int = 32 + +@description('Optional. The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80.') +param vCores int = 4 + +@description('Optional. The license type. Possible values are \'LicenseIncluded\' (regular price inclusive of a new SQL license) and \'BasePrice\' (discounted AHB price for bringing your own SQL licenses).') +@allowed([ + 'LicenseIncluded' + 'BasePrice' +]) +param licenseType string = 'LicenseIncluded' + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param hardwareFamily string = 'Gen5' + +@description('Optional. Whether or not multi-az is enabled.') +param zoneRedundant bool = false + +@description('Optional. Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal.') +@allowed([ + 'None' + 'SystemAssigned' +]) +param servicePrincipal string = 'None' + +@description('Optional. Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified.') +@allowed([ + 'Default' + 'PointInTimeRestore' +]) +param managedInstanceCreateMode string = 'Default' + +@description('Optional. The resource ID of another managed instance whose DNS zone this managed instance will share after creation.') +param dnsZonePartner string = '' + +@description('Optional. Collation of the managed instance.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Connection type used for connecting to the instance.') +@allowed([ + 'Proxy' + 'Redirect' + 'Default' +]) +param proxyOverride string = 'Proxy' + +@description('Optional. Whether or not the public data endpoint is enabled.') +param publicDataEndpointEnabled bool = false + +@description('Optional. ID of the timezone. Allowed values are timezones supported by Windows.') +param timezoneId string = 'UTC' + +@description('Optional. The resource ID of the instance pool this managed server belongs to.') +param instancePoolResourceId string = '' + +@description('Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database.') +param restorePointInTime string = '' + +@description('Optional. The resource identifier of the source managed instance associated with create operation of this instance.') +param sourceManagedInstanceId string = '' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Conditional. The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty.') +param primaryUserAssignedIdentityId string = '' + +@description('Optional. Databases to create in this server.') +param databases array = [] + +@description('Optional. The vulnerability assessment configuration.') +param vulnerabilityAssessmentsObj object = {} + +@description('Optional. The security alert policy configuration.') +param securityAlertPoliciesObj object = {} + +@description('Optional. The keys to configure.') +param keys array = [] + +@description('Optional. The encryption protection configuration.') +param encryptionProtectorObj object = {} + +@description('Optional. The administrator configuration.') +param administratorsObj object = {} + +@description('Optional. The storage account type used to store backups for this database.') +@allowed([ + 'Geo' + 'GeoZone' + 'Local' + 'Zone' +]) +param requestedBackupStorageRedundancy string = 'Geo' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'ResourceUsageStats' + 'SQLSecurityAuditEvents' +]) +param diagnosticLogCategoriesToEnable array = [ + 'ResourceUsageStats' + 'SQLSecurityAuditEvents' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' = { + name: name + location: location + identity: identity + sku: { + name: skuName + tier: skuTier + family: hardwareFamily + } + tags: tags + properties: { + managedInstanceCreateMode: managedInstanceCreateMode + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + subnetId: subnetId + licenseType: licenseType + vCores: vCores + storageSizeInGB: storageSizeInGB + collation: collation + dnsZonePartner: !empty(dnsZonePartner) ? dnsZonePartner : null + publicDataEndpointEnabled: publicDataEndpointEnabled + sourceManagedInstanceId: !empty(sourceManagedInstanceId) ? sourceManagedInstanceId : null + restorePointInTime: !empty(restorePointInTime) ? restorePointInTime : null + proxyOverride: proxyOverride + timezoneId: timezoneId + instancePoolId: !empty(instancePoolResourceId) ? instancePoolResourceId : null + primaryUserAssignedIdentityId: !empty(primaryUserAssignedIdentityId) ? primaryUserAssignedIdentityId : null + requestedBackupStorageRedundancy: requestedBackupStorageRedundancy + zoneRedundant: zoneRedundant + servicePrincipal: { + type: servicePrincipal + } + } +} + +resource managedInstance_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${managedInstance.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: managedInstance +} + +resource managedInstance_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: managedInstance +} + +module managedInstance_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-SqlMi-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: managedInstance.id + } +}] + +module managedInstance_databases 'databases/deploy.bicep' = [for (database, index) in databases: { + name: '${uniqueString(deployment().name, location)}-SqlMi-DB-${index}' + params: { + name: database.name + managedInstanceName: managedInstance.name + catalogCollation: contains(database, 'catalogCollation') ? database.catalogCollation : 'SQL_Latin1_General_CP1_CI_AS' + collation: contains(database, 'collation') ? database.collation : 'SQL_Latin1_General_CP1_CI_AS' + createMode: contains(database, 'createMode') ? database.createMode : 'Default' + diagnosticLogsRetentionInDays: contains(database, 'diagnosticLogsRetentionInDays') ? database.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(database, 'diagnosticStorageAccountId') ? database.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(database, 'diagnosticEventHubAuthorizationRuleId') ? database.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(database, 'diagnosticEventHubName') ? database.diagnosticEventHubName : '' + location: contains(database, 'location') ? database.location : managedInstance.location + lock: contains(database, 'lock') ? database.lock : '' + longTermRetentionBackupResourceId: contains(database, 'longTermRetentionBackupResourceId') ? database.longTermRetentionBackupResourceId : '' + recoverableDatabaseId: contains(database, 'recoverableDatabaseId') ? database.recoverableDatabaseId : '' + restorableDroppedDatabaseId: contains(database, 'restorableDroppedDatabaseId') ? database.restorableDroppedDatabaseId : '' + restorePointInTime: contains(database, 'restorePointInTime') ? database.restorePointInTime : '' + sourceDatabaseId: contains(database, 'sourceDatabaseId') ? database.sourceDatabaseId : '' + storageContainerSasToken: contains(database, 'storageContainerSasToken') ? database.storageContainerSasToken : '' + storageContainerUri: contains(database, 'storageContainerUri') ? database.storageContainerUri : '' + tags: contains(database, 'tags') ? database.tags : {} + diagnosticWorkspaceId: contains(database, 'diagnosticWorkspaceId') ? database.diagnosticWorkspaceId : '' + backupShortTermRetentionPoliciesObj: contains(database, 'backupShortTermRetentionPolicies') ? database.backupShortTermRetentionPolicies : {} + backupLongTermRetentionPoliciesObj: contains(database, 'backupLongTermRetentionPolicies') ? database.backupLongTermRetentionPolicies : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module managedInstance_securityAlertPolicy 'securityAlertPolicies/deploy.bicep' = if (!empty(securityAlertPoliciesObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-SecAlertPol' + params: { + managedInstanceName: managedInstance.name + name: securityAlertPoliciesObj.name + emailAccountAdmins: contains(securityAlertPoliciesObj, 'emailAccountAdmins') ? securityAlertPoliciesObj.emailAccountAdmins : false + state: contains(securityAlertPoliciesObj, 'state') ? securityAlertPoliciesObj.state : 'Disabled' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module managedInstance_vulnerabilityAssessment 'vulnerabilityAssessments/deploy.bicep' = if (!empty(vulnerabilityAssessmentsObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-VulnAssessm' + params: { + managedInstanceName: managedInstance.name + name: vulnerabilityAssessmentsObj.name + recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') ? vulnerabilityAssessmentsObj.recurringScansEmails : [] + recurringScansEmailSubscriptionAdmins: contains(vulnerabilityAssessmentsObj, 'recurringScansEmailSubscriptionAdmins') ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins : false + recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') ? vulnerabilityAssessmentsObj.recurringScansIsEnabled : false + vulnerabilityAssessmentsStorageAccountId: contains(vulnerabilityAssessmentsObj, 'vulnerabilityAssessmentsStorageAccountId') ? vulnerabilityAssessmentsObj.vulnerabilityAssessmentsStorageAccountId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + managedInstance_securityAlertPolicy + ] +} + +module managedInstance_keys 'keys/deploy.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-SqlMi-Key-${index}' + params: { + name: key.name + managedInstanceName: managedInstance.name + serverKeyType: contains(key, 'serverKeyType') ? key.serverKeyType : 'ServiceManaged' + uri: contains(key, 'uri') ? key.uri : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module managedInstance_encryptionProtector 'encryptionProtector/deploy.bicep' = if (!empty(encryptionProtectorObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-EncryProtector' + params: { + managedInstanceName: managedInstance.name + serverKeyName: encryptionProtectorObj.serverKeyName + name: contains(encryptionProtectorObj, 'name') ? encryptionProtectorObj.serverKeyType : 'current' + serverKeyType: contains(encryptionProtectorObj, 'serverKeyType') ? encryptionProtectorObj.serverKeyType : 'ServiceManaged' + autoRotationEnabled: contains(encryptionProtectorObj, 'autoRotationEnabled') ? encryptionProtectorObj.autoRotationEnabled : true + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module managedInstance_administrator 'administrators/deploy.bicep' = if (!empty(administratorsObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-Admin' + params: { + managedInstanceName: managedInstance.name + login: administratorsObj.name + sid: administratorsObj.sid + tenantId: contains(administratorsObj, 'tenantId') ? administratorsObj.tenantId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the deployed managed instance.') +output name string = managedInstance.name + +@description('The resource ID of the deployed managed instance.') +output resourceId string = managedInstance.id + +@description('The resource group of the deployed managed instance.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(managedInstance.identity, 'principalId') ? managedInstance.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = managedInstance.location diff --git a/modules/Microsoft.Sql/managedInstances/encryptionProtector/deploy.bicep b/modules/Microsoft.Sql/managedInstances/encryptionProtector/deploy.bicep new file mode 100644 index 0000000..42cd6dd --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/encryptionProtector/deploy.bicep @@ -0,0 +1,56 @@ +@description('Required. The name of the encryptionProtector.') +param name string = 'current' + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. The name of the SQL managed instance key.') +param serverKeyName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. Key auto rotation opt-in flag.') +param autoRotationEnabled bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource encryptionProtector 'Microsoft.Sql/managedInstances/encryptionProtector@2022-02-01-preview' = { + name: name + parent: managedInstance + properties: { + autoRotationEnabled: autoRotationEnabled + serverKeyName: serverKeyName + serverKeyType: serverKeyType + } +} + +@description('The name of the deployed managed instance encryption protector.') +output name string = encryptionProtector.name + +@description('The resource ID of the deployed managed instance encryption protector.') +output resourceId string = encryptionProtector.id + +@description('The resource group of the deployed managed instance encryption protector.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/encryptionProtector/readme.md b/modules/Microsoft.Sql/managedInstances/encryptionProtector/readme.md new file mode 100644 index 0000000..fe57349 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/encryptionProtector/readme.md @@ -0,0 +1,49 @@ +# SQL Managed Instance Encryption Protector `[Microsoft.Sql/managedInstances/encryptionProtector]` + +This module deploys an encryption protector for a SQL managed instance. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/encryptionProtector` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/encryptionProtector) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `'current'` | The name of the encryptionProtector. | +| `serverKeyName` | string | | The name of the SQL managed instance key. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `autoRotationEnabled` | bool | `False` | | Key auto rotation opt-in flag. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `serverKeyType` | string | `'ServiceManaged'` | `[AzureKeyVault, ServiceManaged]` | The encryption protector type like "ServiceManaged", "AzureKeyVault". | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance encryption protector. | +| `resourceGroupName` | string | The resource group of the deployed managed instance encryption protector. | +| `resourceId` | string | The resource ID of the deployed managed instance encryption protector. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/encryptionProtector/version.json b/modules/Microsoft.Sql/managedInstances/encryptionProtector/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/encryptionProtector/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/keys/deploy.bicep b/modules/Microsoft.Sql/managedInstances/keys/deploy.bicep new file mode 100644 index 0000000..bbe63fe --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/keys/deploy.bicep @@ -0,0 +1,58 @@ +@description('Required. The name of the key. Must follow the [__] pattern.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required.') +param uri string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var splittedKeyUri = split(uri, '/') + +// if serverManaged, use serverManaged, if uri provided use concated uri value +// MUST match the pattern '__' +var serverKeyName = empty(uri) ? 'ServiceManaged' : '${split(splittedKeyUri[2], '.')[0]}_${splittedKeyUri[4]}_${splittedKeyUri[5]}' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource key 'Microsoft.Sql/managedInstances/keys@2022-02-01-preview' = { + name: !empty(name) ? name : serverKeyName + parent: managedInstance + properties: { + serverKeyType: serverKeyType + uri: uri + } +} + +@description('The name of the deployed managed instance key.') +output name string = key.name + +@description('The resource ID of the deployed managed instance key.') +output resourceId string = key.id + +@description('The resource group of the deployed managed instance key.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/keys/readme.md b/modules/Microsoft.Sql/managedInstances/keys/readme.md new file mode 100644 index 0000000..975789e --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/keys/readme.md @@ -0,0 +1,48 @@ +# SQL Managed Instance Keys `[Microsoft.Sql/managedInstances/keys]` + +This module deploys a key for a SQL managed instance. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/keys` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/keys) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. Must follow the [__] pattern. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `serverKeyType` | string | `'ServiceManaged'` | `[AzureKeyVault, ServiceManaged]` | The encryption protector type like "ServiceManaged", "AzureKeyVault". | +| `uri` | string | `''` | | The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance key. | +| `resourceGroupName` | string | The resource group of the deployed managed instance key. | +| `resourceId` | string | The resource ID of the deployed managed instance key. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/keys/version.json b/modules/Microsoft.Sql/managedInstances/keys/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/keys/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/readme.md b/modules/Microsoft.Sql/managedInstances/readme.md new file mode 100644 index 0000000..72922e4 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/readme.md @@ -0,0 +1,605 @@ +# SQL Managed Instances `[Microsoft.Sql/managedInstances]` + +This template deploys a SQL managed instance. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances) | +| `Microsoft.Sql/managedInstances/administrators` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/administrators) | +| `Microsoft.Sql/managedInstances/databases` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/databases/backupShortTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/encryptionProtector` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/encryptionProtector) | +| `Microsoft.Sql/managedInstances/keys` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/keys) | +| `Microsoft.Sql/managedInstances/securityAlertPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/securityAlertPolicies) | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/vulnerabilityAssessments) | + +### Deployment prerequisites + +#### Networking + +SQL Managed Instance is deployed on a virtual network to a subnet that is delagated to the SQL MI service. This network is required to satisfy the requirements explained [here](https://docs.microsoft.com/en-us/azure/azure-sql/managed-instance/connectivity-architecture-overview?view=azuresql#network-requirements). + +SQL MI requires that the subnet have a Route Table and NSG assigned to it. The SQL MI service will automatically add Routes to the Route Table and Rules to the NSG once the SQL MI has been deployed. As a result, the parameter file for the Route Table and NSG will have to be updated afterwards with the created Routes & Rules, otherwise redeployment of the Route Table & NSG via Bicep/ARM will fail. + +#### Azure AD Authentication + +SQL MI allows for Azure AD Authentication via an [Azure AD Admin](https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-configure?tabs=azure-powershell#provision-azure-ad-admin-sql-managed-instance). This requires a Service Principal to be assigned and granted Reader rights to Azure AD by an AD Admin. To do so via this module, the `servicePrincipal` parameter must be set to `SystemAssigned` and deploy the SQL MI. Afterwards an Azure AD Admin must go to the SQL MI Azure Active Directory admin page in the Azure Portal and assigned the Reader rights. Next the `administratorsObj` must be configured in the parameter file and be redeployed. + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `administratorLogin` | string | The username used to establish jumpbox VMs. | +| `administratorLoginPassword` | secureString | The password given to the admin user. | +| `name` | string | The name of the SQL managed instance. | +| `subnetId` | string | The fully qualified resource ID of the subnet on which the SQL managed instance will be placed. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `primaryUserAssignedIdentityId` | string | `''` | The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `administratorsObj` | _[administrators](administrators/readme.md)_ object | `{object}` | | The administrator configuration. | +| `collation` | string | `'SQL_Latin1_General_CP1_CI_AS'` | | Collation of the managed instance. | +| `databases` | _[databases](databases/readme.md)_ array | `[]` | | Databases to create in this server. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[ResourceUsageStats, SQLSecurityAuditEvents]` | `[ResourceUsageStats, SQLSecurityAuditEvents]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `dnsZonePartner` | string | `''` | | The resource ID of another managed instance whose DNS zone this managed instance will share after creation. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `encryptionProtectorObj` | _[encryptionProtector](encryptionProtector/readme.md)_ object | `{object}` | | The encryption protection configuration. | +| `hardwareFamily` | string | `'Gen5'` | | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| `instancePoolResourceId` | string | `''` | | The resource ID of the instance pool this managed server belongs to. | +| `keys` | _[keys](keys/readme.md)_ array | `[]` | | The keys to configure. | +| `licenseType` | string | `'LicenseIncluded'` | `[BasePrice, LicenseIncluded]` | The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedInstanceCreateMode` | string | `'Default'` | `[Default, PointInTimeRestore]` | Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified. | +| `proxyOverride` | string | `'Proxy'` | `[Default, Proxy, Redirect]` | Connection type used for connecting to the instance. | +| `publicDataEndpointEnabled` | bool | `False` | | Whether or not the public data endpoint is enabled. | +| `requestedBackupStorageRedundancy` | string | `'Geo'` | `[Geo, GeoZone, Local, Zone]` | The storage account type used to store backups for this database. | +| `restorePointInTime` | string | `''` | | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. | +| `roleAssignments` | array | `[]` | | 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'. | +| `securityAlertPoliciesObj` | _[securityAlertPolicies](securityAlertPolicies/readme.md)_ object | `{object}` | | The security alert policy configuration. | +| `servicePrincipal` | string | `'None'` | `[None, SystemAssigned]` | Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal. | +| `skuName` | string | `'GP_Gen5'` | | The name of the SKU, typically, a letter + Number code, e.g. P3. | +| `skuTier` | string | `'GeneralPurpose'` | | The tier or edition of the particular SKU, e.g. Basic, Premium. | +| `sourceManagedInstanceId` | string | `''` | | The resource identifier of the source managed instance associated with create operation of this instance. | +| `storageSizeInGB` | int | `32` | | Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timezoneId` | string | `'UTC'` | | ID of the timezone. Allowed values are timezones supported by Windows. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `vCores` | int | `4` | | The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80. | +| `vulnerabilityAssessmentsObj` | _[vulnerabilityAssessments](vulnerabilityAssessments/readme.md)_ object | `{object}` | | The vulnerability assessment configuration. | +| `zoneRedundant` | bool | `False` | | Whether or not multi-az is enabled. | + + +### Parameter Usage : `userAssignedIdentities` + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed managed instance. | +| `resourceGroupName` | string | The resource group of the deployed managed instance. | +| `resourceId` | string | The resource ID of the deployed managed instance. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module managedInstances './Microsoft.Sql/managedInstances/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ManagedInstances' + params: { + // Required parameters + administratorLogin: kv1.getSecret('administratorLogin') + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + name: '<>-az-sqlmi-min-001' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi' + } +} +``` + +
+

+ +

+ +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 + "administratorLogin": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLogin" + } + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "name": { + "value": "<>-az-sqlmi-min-001" + }, + "subnetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','validation-rg') +} + +module managedInstances './Microsoft.Sql/managedInstances/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ManagedInstances' + params: { + administratorLogin: kv1.getSecret('administratorLogin') + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + name: '<>-az-sqlmi-x-001' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi' + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + name: '<>-az-sqlmidb-x-001' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: 'adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: 'adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07' + serverKeyType: 'AzureKeyVault' + uri: 'https://adp-<>-az-kv-x-sqlmi.vault.azure.net/keys/keyEncryptionKeySqlMi/4bf367f64c914d8ba698700fb598ad07' + } + ] + licenseType: 'LicenseIncluded' + lock: 'CanNotDelete' + primaryUserAssignedIdentityId: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + systemAssignedIdentity: true + timezoneId: 'UTC' + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + vulnerabilityAssessmentsStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "administratorLogin": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLogin" + } + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "name": { + "value": "<>-az-sqlmi-x-001" + }, + "subnetId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-sqlmi/subnets/<>-az-subnet-x-sqlmi" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "databases": { + "value": [ + { + "backupLongTermRetentionPolicies": { + "name": "default" + }, + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "name": "<>-az-sqlmidb-x-001" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "dnsZonePartner": { + "value": "" + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07", + "serverKeyType": "AzureKeyVault" + } + }, + "hardwareFamily": { + "value": "Gen5" + }, + "keys": { + "value": [ + { + "name": "adp-<>-az-kv-x-sqlmi_keyEncryptionKeySqlMi_4bf367f64c914d8ba698700fb598ad07", + "serverKeyType": "AzureKeyVault", + "uri": "https://adp-<>-az-kv-x-sqlmi.vault.azure.net/keys/keyEncryptionKeySqlMi/4bf367f64c914d8ba698700fb598ad07" + } + ] + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "lock": { + "value": "CanNotDelete" + }, + "primaryUserAssignedIdentityId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "proxyOverride": { + "value": "Proxy" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "systemAssignedIdentity": { + "value": true + }, + "timezoneId": { + "value": "UTC" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "vCores": { + "value": 4 + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "vulnerabilityAssessmentsStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/deploy.bicep b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/deploy.bicep new file mode 100644 index 0000000..ad40235 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/deploy.bicep @@ -0,0 +1,52 @@ +@description('Required. The name of the security alert policy.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param state string = 'Disabled' + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param emailAccountAdmins bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource securityAlertPolicy 'Microsoft.Sql/managedInstances/securityAlertPolicies@2022-02-01-preview' = { + name: name + parent: managedInstance + properties: { + state: state + emailAccountAdmins: emailAccountAdmins + } +} + +@description('The name of the deployed security alert policy.') +output name string = securityAlertPolicy.name + +@description('The resource ID of the deployed security alert policy.') +output resourceId string = securityAlertPolicy.id + +@description('The resource group of the deployed security alert policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/readme.md b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/readme.md new file mode 100644 index 0000000..d5a49f9 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/readme.md @@ -0,0 +1,48 @@ +# SQL Managed Instance Security Alert Policy `[Microsoft.Sql/managedInstances/securityAlertPolicies]` + +This module deploys a security alert policy for a SQL managed instance. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/securityAlertPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/securityAlertPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the security alert policy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `emailAccountAdmins` | bool | `False` | | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `state` | string | `'Disabled'` | `[Disabled, Enabled]` | Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed security alert policy. | +| `resourceGroupName` | string | The resource group of the deployed security alert policy. | +| `resourceId` | string | The resource ID of the deployed security alert policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/version.json b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/securityAlertPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/version.json b/modules/Microsoft.Sql/managedInstances/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/deploy.bicep b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/deploy.bicep new file mode 100644 index 0000000..f5820f6 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/deploy.bicep @@ -0,0 +1,59 @@ +@description('Required. The name of the vulnerability assessment.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Recurring scans state.') +param recurringScansIsEnabled bool = false + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param recurringScansEmailSubscriptionAdmins bool = false + +@description('Optional. Specifies an array of email addresses to which the scan notification is sent.') +param recurringScansEmails array = [] + +@description('Optional. A blob storage to hold the scan results.') +param vulnerabilityAssessmentsStorageAccountId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-02-01-preview' existing = { + name: managedInstanceName +} + +resource vulnerabilityAssessment 'Microsoft.Sql/managedInstances/vulnerabilityAssessments@2022-02-01-preview' = { + name: name + parent: managedInstance + properties: { + storageContainerPath: 'https://${last(split(vulnerabilityAssessmentsStorageAccountId, '/'))}.blob.${environment().suffixes.storage}/vulnerability-assessment/' + storageAccountAccessKey: listKeys(vulnerabilityAssessmentsStorageAccountId, '2019-06-01').keys[0].value + recurringScans: { + isEnabled: recurringScansIsEnabled + emailSubscriptionAdmins: recurringScansEmailSubscriptionAdmins + emails: recurringScansEmails + } + } +} + +@description('The name of the deployed vulnerability assessment.') +output name string = vulnerabilityAssessment.name + +@description('The resource ID of the deployed vulnerability assessment.') +output resourceId string = vulnerabilityAssessment.id + +@description('The resource group of the deployed vulnerability assessment.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/readme.md b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/readme.md new file mode 100644 index 0000000..8e1bcf9 --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/readme.md @@ -0,0 +1,50 @@ +# SQL Managed Instance Vulnerability Assessments `[Microsoft.Sql/managedInstances/vulnerabilityAssessments]` + +This module deploys a vulnerability assessment for a SQL managed instance. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/managedInstances/vulnerabilityAssessments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the vulnerability assessment. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `managedInstanceName` | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `recurringScansEmails` | array | `[]` | Specifies an array of email addresses to which the scan notification is sent. | +| `recurringScansEmailSubscriptionAdmins` | bool | `False` | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| `recurringScansIsEnabled` | bool | `False` | Recurring scans state. | +| `vulnerabilityAssessmentsStorageAccountId` | string | `''` | A blob storage to hold the scan results. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed vulnerability assessment. | +| `resourceGroupName` | string | The resource group of the deployed vulnerability assessment. | +| `resourceId` | string | The resource ID of the deployed vulnerability assessment. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/version.json b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/managedInstances/vulnerabilityAssessments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/servers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Sql/servers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..da3439a --- /dev/null +++ b/modules/Microsoft.Sql/servers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'SQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'SQL Server Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(server.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: server +}] diff --git a/modules/Microsoft.Sql/servers/.test/admin.parameters.json b/modules/Microsoft.Sql/servers/.test/admin.parameters.json new file mode 100644 index 0000000..eadb38d --- /dev/null +++ b/modules/Microsoft.Sql/servers/.test/admin.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sqlsrv-admin-001" + }, + "administrators": { + "value": { + "azureADOnlyAuthentication": true, + "login": "myspn", + "sid": "<>", + "principalType": "Application", + "tenantId": "<>" + } + } + } +} diff --git a/modules/Microsoft.Sql/servers/.test/parameters.json b/modules/Microsoft.Sql/servers/.test/parameters.json new file mode 100644 index 0000000..c1e8f21 --- /dev/null +++ b/modules/Microsoft.Sql/servers/.test/parameters.json @@ -0,0 +1,118 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sqlsrv-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/<>/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "location": { + "value": "westeurope" + }, + "minimalTlsVersion": { + "value": "1.2" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "vulnerabilityAssessmentsObj": { + "value": { + "name": "default", + "emailSubscriptionAdmins": true, + "recurringScansIsEnabled": true, + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "vulnerabilityAssessmentsStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + }, + "databases": { + "value": [ + { + "name": "<>-az-sqldb-x-001", + "collation": "SQL_Latin1_General_CP1_CI_AS", + "skuTier": "BusinessCritical", + "skuName": "BC_Gen5", + "skuCapacity": 12, + "skuFamily": "Gen5", + "maxSizeBytes": 34359738368, + "licenseType": "LicenseIncluded", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001" + } + ] + }, + "firewallRules": { + "value": [ + { + "name": "AllowAllWindowsAzureIps", + "endIpAddress": "0.0.0.0", + "startIpAddress": "0.0.0.0" + } + ] + }, + "virtualNetworkRules": { + "value": [ + { + "name": "newVnetRule1", + "ignoreMissingVnetServiceEndpoint": true, + "virtualNetworkSubnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "securityAlertPolicies": { + "value": [ + { + "name": "Default", + "state": "Enabled", + "emailAccountAdmins": true + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "sqlServer", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Sql/servers/.test/pe.parameters.json b/modules/Microsoft.Sql/servers/.test/pe.parameters.json new file mode 100644 index 0000000..1056857 --- /dev/null +++ b/modules/Microsoft.Sql/servers/.test/pe.parameters.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-sqlsrv-pe-001" + }, + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/<>/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "sqlServer", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Sql/servers/databases/deploy.bicep b/modules/Microsoft.Sql/servers/databases/deploy.bicep new file mode 100644 index 0000000..ea106dd --- /dev/null +++ b/modules/Microsoft.Sql/servers/databases/deploy.bicep @@ -0,0 +1,230 @@ +@description('Required. The name of the database.') +param name string + +@description('Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment.') +param serverName string + +@description('Optional. The collation of the database.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. The skuTier or edition of the particular SKU.') +param skuTier string = 'GeneralPurpose' + +@description('Optional. The name of the SKU.') +param skuName string = 'GP_Gen5_2' + +@description('Optional. Capacity of the particular SKU.') +param skuCapacity int = -1 + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param skuFamily string = '' + +@description('Optional. Size of the particular SKU.') +param skuSize string = '' + +@description('Optional. The max size of the database expressed in bytes.') +param maxSizeBytes int = 34359738368 + +@description('Optional. The name of the sample schema to apply when creating this database.') +param sampleName string = '' + +@description('Optional. Whether or not this database is zone redundant.') +param zoneRedundant bool = false + +@description('Optional. The license type to apply for this database.') +param licenseType string = '' + +@description('Optional. The state of read-only routing.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param readScale string = 'Disabled' + +@description('Optional. The number of readonly secondary replicas associated with the database.') +param highAvailabilityReplicaCount int = 0 + +@description('Optional. Minimal capacity that database will always have allocated.') +param minCapacity string = '' + +@description('Optional. Time in minutes after which database is automatically paused.') +param autoPauseDelay string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'SQLInsights' + 'AutomaticTuning' + 'QueryStoreRuntimeStatistics' + 'QueryStoreWaitStatistics' + 'Errors' + 'DatabaseWaitStatistics' + 'Timeouts' + 'Blocks' + 'Deadlocks' + 'DevOpsOperationsAudit' + 'SQLSecurityAuditEvents' +]) +param diagnosticLogCategoriesToEnable array = [ + 'SQLInsights' + 'AutomaticTuning' + 'QueryStoreRuntimeStatistics' + 'QueryStoreWaitStatistics' + 'Errors' + 'DatabaseWaitStatistics' + 'Timeouts' + 'Blocks' + 'Deadlocks' + 'DevOpsOperationsAudit' + 'SQLSecurityAuditEvents' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Basic' + 'InstanceAndAppAdvanced' + 'WorkloadManagement' +]) +param diagnosticMetricsToEnable array = [ + 'Basic' + 'InstanceAndAppAdvanced' + 'WorkloadManagement' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +@description('Optional. The storage account type to be used to store backups for this database.') +@allowed([ + 'Geo' + 'Local' + 'Zone' + '' +]) +param requestedBackupStorageRedundancy string = '' + +@description('Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables. Note: the value of this property cannot be changed after the database has been created.') +param isLedgerOn bool = false + +@description('Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur.') +param maintenanceConfigurationId string = '' + +// The SKU object must be built in a variable +// The alternative, 'null' as default values, leads to non-terminating deployments +var skuVar = union({ + name: skuName + tier: skuTier + }, (skuCapacity != -1) ? { + capacity: skuCapacity + } : !empty(skuFamily) ? { + family: skuFamily + } : !empty(skuSize) ? { + size: skuSize + } : {}) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: serverName +} + +resource database 'Microsoft.Sql/servers/databases@2022-02-01-preview' = { + name: name + parent: server + location: location + tags: tags + properties: { + collation: collation + maxSizeBytes: maxSizeBytes + sampleName: sampleName + zoneRedundant: zoneRedundant + licenseType: licenseType + readScale: readScale + minCapacity: !empty(minCapacity) ? json(minCapacity) : 0 + autoPauseDelay: !empty(autoPauseDelay) ? json(autoPauseDelay) : 0 + highAvailabilityReplicaCount: highAvailabilityReplicaCount + requestedBackupStorageRedundancy: any(requestedBackupStorageRedundancy) + isLedgerOn: isLedgerOn + maintenanceConfigurationId: !empty(maintenanceConfigurationId) ? maintenanceConfigurationId : null + } + sku: skuVar +} + +resource database_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: database +} + +@description('The name of the deployed database.') +output name string = database.name + +@description('The resource ID of the deployed database.') +output resourceId string = database.id + +@description('The resource group of the deployed database.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = database.location diff --git a/modules/Microsoft.Sql/servers/databases/readme.md b/modules/Microsoft.Sql/servers/databases/readme.md new file mode 100644 index 0000000..a5de6b7 --- /dev/null +++ b/modules/Microsoft.Sql/servers/databases/readme.md @@ -0,0 +1,116 @@ +# SQL Server Database `[Microsoft.Sql/servers/databases]` + +This module deploys an Azure SQL Server. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/servers/databases` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/databases) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the database. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serverName` | string | The name of the parent SQL Server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `autoPauseDelay` | string | `''` | | Time in minutes after which database is automatically paused. | +| `collation` | string | `'SQL_Latin1_General_CP1_CI_AS'` | | The collation of the database. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AutomaticTuning, Blocks, DatabaseWaitStatistics, Deadlocks, DevOpsOperationsAudit, Errors, QueryStoreRuntimeStatistics, QueryStoreWaitStatistics, SQLInsights, SQLSecurityAuditEvents, Timeouts]` | `[AutomaticTuning, Blocks, DatabaseWaitStatistics, Deadlocks, DevOpsOperationsAudit, Errors, QueryStoreRuntimeStatistics, QueryStoreWaitStatistics, SQLInsights, SQLSecurityAuditEvents, Timeouts]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Basic, InstanceAndAppAdvanced, WorkloadManagement]` | `[Basic, InstanceAndAppAdvanced, WorkloadManagement]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `highAvailabilityReplicaCount` | int | `0` | | The number of readonly secondary replicas associated with the database. | +| `isLedgerOn` | bool | `False` | | Whether or not this database is a ledger database, which means all tables in the database are ledger tables. Note: the value of this property cannot be changed after the database has been created. | +| `licenseType` | string | `''` | | The license type to apply for this database. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `maintenanceConfigurationId` | string | `''` | | Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur. | +| `maxSizeBytes` | int | `34359738368` | | The max size of the database expressed in bytes. | +| `minCapacity` | string | `''` | | Minimal capacity that database will always have allocated. | +| `readScale` | string | `'Disabled'` | `[Disabled, Enabled]` | The state of read-only routing. | +| `requestedBackupStorageRedundancy` | string | `''` | `['', Geo, Local, Zone]` | The storage account type to be used to store backups for this database. | +| `sampleName` | string | `''` | | The name of the sample schema to apply when creating this database. | +| `skuCapacity` | int | `-1` | | Capacity of the particular SKU. | +| `skuFamily` | string | `''` | | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| `skuName` | string | `'GP_Gen5_2'` | | The name of the SKU. | +| `skuSize` | string | `''` | | Size of the particular SKU. | +| `skuTier` | string | `'GeneralPurpose'` | | The skuTier or edition of the particular SKU. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `zoneRedundant` | bool | `False` | | Whether or not this database is zone redundant. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed database. | +| `resourceGroupName` | string | The resource group of the deployed database. | +| `resourceId` | string | The resource ID of the deployed database. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/databases/version.json b/modules/Microsoft.Sql/servers/databases/version.json new file mode 100644 index 0000000..a086a18 --- /dev/null +++ b/modules/Microsoft.Sql/servers/databases/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "1.0" +} diff --git a/modules/Microsoft.Sql/servers/deploy.bicep b/modules/Microsoft.Sql/servers/deploy.bicep new file mode 100644 index 0000000..340c632 --- /dev/null +++ b/modules/Microsoft.Sql/servers/deploy.bicep @@ -0,0 +1,261 @@ +@description('Conditional. The administrator username for the server. Required if no `administrators` object for AAD authentication is provided.') +param administratorLogin string = '' + +@description('Conditional. The administrator login password. Required if no `administrators` object for AAD authentication is provided.') +@secure() +param administratorLoginPassword string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The name of the server.') +param name string + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The databases to create in the server.') +param databases array = [] + +@description('Optional. The firewall rules to create in the server.') +param firewallRules array = [] + +@description('Optional. The virtual network rules to create in the server.') +param virtualNetworkRules array = [] + +@description('Optional. The security alert policies to create in the server.') +param securityAlertPolicies array = [] + +@description('Conditional. The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided.') +param administrators object = {} + +@allowed([ + '1.0' + '1.1' + '1.2' +]) +@description('Optional. Minimal TLS version allowed.') +param minimalTlsVersion string = '1.2' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and neither firewall rules nor virtual network rules are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +@description('Optional. The vulnerability assessment configuration.') +param vulnerabilityAssessmentsObj object = {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' = { + location: location + name: name + tags: tags + identity: identity + properties: { + administratorLogin: !empty(administratorLogin) ? administratorLogin : null + administratorLoginPassword: !empty(administratorLoginPassword) ? administratorLoginPassword : null + administrators: !empty(administrators) ? { + administratorType: 'ActiveDirectory' + azureADOnlyAuthentication: administrators.azureADOnlyAuthentication + login: administrators.login + principalType: administrators.principalType + sid: administrators.sid + tenantId: administrators.tenantId + } : null + version: '12.0' + minimalTlsVersion: minimalTlsVersion + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(firewallRules) && empty(virtualNetworkRules) ? 'Disabled' : null) + } +} + +resource server_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${server.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: server +} + +module server_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Sql-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: server.id + } +}] + +module server_databases 'databases/deploy.bicep' = [for (database, index) in databases: { + name: '${uniqueString(deployment().name, location)}-Sql-DB-${index}' + params: { + name: database.name + serverName: server.name + skuTier: contains(database, 'skuTier') ? database.skuTier : 'GeneralPurpose' + skuName: contains(database, 'skuName') ? database.skuName : 'GP_Gen5_2' + skuCapacity: contains(database, 'skuCapacity') ? database.skuCapacity : -1 + skuFamily: contains(database, 'skuFamily') ? database.skuFamily : '' + skuSize: contains(database, 'skuSize') ? database.skuSize : '' + collation: contains(database, 'collation') ? database.collation : 'SQL_Latin1_General_CP1_CI_AS' + maxSizeBytes: contains(database, 'maxSizeBytes') ? database.maxSizeBytes : 34359738368 + autoPauseDelay: contains(database, 'autoPauseDelay') ? database.autoPauseDelay : '' + diagnosticLogsRetentionInDays: contains(database, 'diagnosticLogsRetentionInDays') ? database.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(database, 'diagnosticStorageAccountId') ? database.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(database, 'diagnosticEventHubAuthorizationRuleId') ? database.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(database, 'diagnosticEventHubName') ? database.diagnosticEventHubName : '' + isLedgerOn: contains(database, 'isLedgerOn') ? database.isLedgerOn : false + location: contains(database, 'location') ? database.location : server.location + diagnosticLogCategoriesToEnable: contains(database, 'diagnosticLogCategoriesToEnable') ? database.diagnosticLogCategoriesToEnable : [] + licenseType: contains(database, 'licenseType') ? database.licenseType : '' + maintenanceConfigurationId: contains(database, 'maintenanceConfigurationId') ? database.maintenanceConfigurationId : '' + minCapacity: contains(database, 'minCapacity') ? database.minCapacity : '' + diagnosticMetricsToEnable: contains(database, 'diagnosticMetricsToEnable') ? database.diagnosticMetricsToEnable : [] + highAvailabilityReplicaCount: contains(database, 'highAvailabilityReplicaCount') ? database.highAvailabilityReplicaCount : 0 + readScale: contains(database, 'readScale') ? database.readScale : 'Disabled' + requestedBackupStorageRedundancy: contains(database, 'requestedBackupStorageRedundancy') ? database.requestedBackupStorageRedundancy : '' + sampleName: contains(database, 'sampleName') ? database.sampleName : '' + tags: contains(database, 'tags') ? database.tags : {} + diagnosticWorkspaceId: contains(database, 'diagnosticWorkspaceId') ? database.diagnosticWorkspaceId : '' + zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module server_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-SQLServer-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(server.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: server.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +module server_firewallRules 'firewallRules/deploy.bicep' = [for (firewallRule, index) in firewallRules: { + name: '${uniqueString(deployment().name, location)}-Sql-FirewallRules-${index}' + params: { + name: firewallRule.name + serverName: server.name + endIpAddress: contains(firewallRule, 'endIpAddress') ? firewallRule.endIpAddress : '0.0.0.0' + startIpAddress: contains(firewallRule, 'startIpAddress') ? firewallRule.startIpAddress : '0.0.0.0' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module server_virtualNetworkRules 'virtualNetworkRules/deploy.bicep' = [for (virtualNetworkRule, index) in virtualNetworkRules: { + name: '${uniqueString(deployment().name, location)}-Sql-VirtualNetworkRules-${index}' + params: { + name: virtualNetworkRule.name + serverName: server.name + ignoreMissingVnetServiceEndpoint: contains(virtualNetworkRule, 'ignoreMissingVnetServiceEndpoint') ? virtualNetworkRule.ignoreMissingVnetServiceEndpoint : false + virtualNetworkSubnetId: virtualNetworkRule.virtualNetworkSubnetId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module server_securityAlertPolicies 'securityAlertPolicies/deploy.bicep' = [for (securityAlertPolicy, index) in securityAlertPolicies: { + name: '${uniqueString(deployment().name, location)}-Sql-SecAlertPolicy-${index}' + params: { + name: securityAlertPolicy.name + serverName: server.name + disabledAlerts: contains(securityAlertPolicy, 'disabledAlerts') ? securityAlertPolicy.disabledAlerts : [] + emailAccountAdmins: contains(securityAlertPolicy, 'emailAccountAdmins') ? securityAlertPolicy.emailAccountAdmins : false + emailAddresses: contains(securityAlertPolicy, 'emailAddresses') ? securityAlertPolicy.emailAddresses : [] + retentionDays: contains(securityAlertPolicy, 'retentionDays') ? securityAlertPolicy.retentionDays : 0 + state: contains(securityAlertPolicy, 'state') ? securityAlertPolicy.state : 'Disabled' + storageAccountAccessKey: contains(securityAlertPolicy, 'storageAccountAccessKey') ? securityAlertPolicy.storageAccountAccessKey : '' + storageEndpoint: contains(securityAlertPolicy, 'storageEndpoint') ? securityAlertPolicy.storageEndpoint : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module server_vulnerabilityAssessment 'vulnerabilityAssessments/deploy.bicep' = if (!empty(vulnerabilityAssessmentsObj)) { + name: '${uniqueString(deployment().name, location)}-Sql-VulnAssessm' + params: { + serverName: server.name + name: vulnerabilityAssessmentsObj.name + recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') ? vulnerabilityAssessmentsObj.recurringScansEmails : [] + recurringScansEmailSubscriptionAdmins: contains(vulnerabilityAssessmentsObj, 'recurringScansEmailSubscriptionAdmins') ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins : false + recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') ? vulnerabilityAssessmentsObj.recurringScansIsEnabled : false + vulnerabilityAssessmentsStorageAccountId: contains(vulnerabilityAssessmentsObj, 'vulnerabilityAssessmentsStorageAccountId') ? vulnerabilityAssessmentsObj.vulnerabilityAssessmentsStorageAccountId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + server_securityAlertPolicies + ] +} + +@description('The name of the deployed SQL server.') +output name string = server.name + +@description('The resource ID of the deployed SQL server.') +output resourceId string = server.id + +@description('The resource group of the deployed SQL server.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(server.identity, 'principalId') ? server.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = server.location diff --git a/modules/Microsoft.Sql/servers/firewallRules/deploy.bicep b/modules/Microsoft.Sql/servers/firewallRules/deploy.bicep new file mode 100644 index 0000000..89ae712 --- /dev/null +++ b/modules/Microsoft.Sql/servers/firewallRules/deploy.bicep @@ -0,0 +1,48 @@ +@description('Required. The name of the Server Firewall Rule.') +param name string + +@description('Optional. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value \'0.0.0.0\' for all Azure-internal IP addresses.') +param endIpAddress string = '0.0.0.0' + +@description('Optional. The start IP address of the firewall rule. Must be IPv4 format. Use value \'0.0.0.0\' for all Azure-internal IP addresses.') +param startIpAddress string = '0.0.0.0' + +@description('Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment.') +param serverName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: serverName +} + +resource firewallRule 'Microsoft.Sql/servers/firewallRules@2022-02-01-preview' = { + name: name + parent: server + properties: { + endIpAddress: endIpAddress + startIpAddress: startIpAddress + } +} + +@description('The name of the deployed firewall rule.') +output name string = firewallRule.name + +@description('The resource ID of the deployed firewall rule.') +output resourceId string = firewallRule.id + +@description('The resource group of the deployed firewall rule.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/servers/firewallRules/readme.md b/modules/Microsoft.Sql/servers/firewallRules/readme.md new file mode 100644 index 0000000..d5564ba --- /dev/null +++ b/modules/Microsoft.Sql/servers/firewallRules/readme.md @@ -0,0 +1,48 @@ +# SQL Server Firewall rule `[Microsoft.Sql/servers/firewallrules]` + +This module deploys an SQL Server Firewall rule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/servers/firewallRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/firewallRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Server Firewall Rule. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serverName` | string | The name of the parent SQL Server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `endIpAddress` | string | `'0.0.0.0'` | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses. | +| `startIpAddress` | string | `'0.0.0.0'` | The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed firewall rule. | +| `resourceGroupName` | string | The resource group of the deployed firewall rule. | +| `resourceId` | string | The resource ID of the deployed firewall rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/firewallRules/version.json b/modules/Microsoft.Sql/servers/firewallRules/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/servers/firewallRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/servers/readme.md b/modules/Microsoft.Sql/servers/readme.md new file mode 100644 index 0000000..00cb8c5 --- /dev/null +++ b/modules/Microsoft.Sql/servers/readme.md @@ -0,0 +1,711 @@ +# SQL Servers `[Microsoft.Sql/servers]` + +This module deploys a SQL server. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Sql/servers` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers) | +| `Microsoft.Sql/servers/databases` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/databases) | +| `Microsoft.Sql/servers/firewallRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/firewallRules) | +| `Microsoft.Sql/servers/securityAlertPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/securityAlertPolicies) | +| `Microsoft.Sql/servers/virtualNetworkRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/virtualNetworkRules) | +| `Microsoft.Sql/servers/vulnerabilityAssessments` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/vulnerabilityAssessments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the server. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `administratorLogin` | string | `''` | The administrator username for the server. Required if no `administrators` object for AAD authentication is provided. | +| `administratorLoginPassword` | secureString | `''` | The administrator login password. Required if no `administrators` object for AAD authentication is provided. | +| `administrators` | object | `{object}` | The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `databases` | _[databases](databases/readme.md)_ array | `[]` | | The databases to create in the server. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `firewallRules` | _[firewallRules](firewallRules/readme.md)_ array | `[]` | | The firewall rules to create in the server. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `minimalTlsVersion` | string | `'1.2'` | `[1.0, 1.1, 1.2]` | Minimal TLS version allowed. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and neither firewall rules nor virtual network rules are set. | +| `roleAssignments` | array | `[]` | | 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'. | +| `securityAlertPolicies` | _[securityAlertPolicies](securityAlertPolicies/readme.md)_ array | `[]` | | The security alert policies to create in the server. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `virtualNetworkRules` | _[virtualNetworkRules](virtualNetworkRules/readme.md)_ array | `[]` | | The virtual network rules to create in the server. | +| `vulnerabilityAssessmentsObj` | _[vulnerabilityAssessments](vulnerabilityAssessments/readme.md)_ object | `{object}` | | The vulnerability assessment configuration. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `administrators` + +Configure Azure Active Directory Authentication method for server administrator. +https://docs.microsoft.com/en-us/azure/templates/microsoft.sql/servers/administrators?tabs=bicep + +

+ +Parameter JSON format + +```json +"administrators": { + "value": { + "azureADOnlyAuthentication": true + "login": "John Doe", // if application can be anything + "sid": "<>", // if application, the object ID + "principalType" : "User", // options: "User", "Group", "Application" + "tenantId": "<>" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +administrators: { + azureADOnlyAuthentication: true + login: 'John Doe' // if application can be anything + sid: '<>' // if application the object ID + 'principalType' : 'User' // options: 'User' 'Group' 'Application' + tenantId: '<>' +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed SQL server. | +| `resourceGroupName` | string | The resource group of the deployed SQL server. | +| `resourceId` | string | The resource ID of the deployed SQL server. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Admin

+ +
+ +via Bicep module + +```bicep +module servers './Microsoft.Sql/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + // Required parameters + name: '<>-az-sqlsrv-admin-001' + // Non-required parameters + administrators: { + azureADOnlyAuthentication: true + login: 'myspn' + principalType: 'Application' + sid: '<>' + tenantId: '<>' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-sqlsrv-admin-001" + }, + // Non-required parameters + "administrators": { + "value": { + "azureADOnlyAuthentication": true, + "login": "myspn", + "principalType": "Application", + "sid": "<>", + "tenantId": "<>" + } + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','<>') +} + +module servers './Microsoft.Sql/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + // Required parameters + name: '<>-az-sqlsrv-x-001' + // Non-required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + databases: [ + { + collation: 'SQL_Latin1_General_CP1_CI_AS' + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + licenseType: 'LicenseIncluded' + maxSizeBytes: 34359738368 + name: '<>-az-sqldb-x-001' + skuCapacity: 12 + skuFamily: 'Gen5' + skuName: 'BC_Gen5' + skuTier: 'BusinessCritical' + } + ] + firewallRules: [ + { + endIpAddress: '0.0.0.0' + name: 'AllowAllWindowsAzureIps' + startIpAddress: '0.0.0.0' + } + ] + location: 'westeurope' + lock: 'CanNotDelete' + minimalTlsVersion: '1.2' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net' + ] + } + service: 'sqlServer' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + securityAlertPolicies: [ + { + emailAccountAdmins: true + name: 'Default' + state: 'Enabled' + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + virtualNetworkRules: [ + { + ignoreMissingVnetServiceEndpoint: true + name: 'newVnetRule1' + virtualNetworkSubnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + vulnerabilityAssessmentsStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + } + } +} +``` + +
+

+ +

+ +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": "<>-az-sqlsrv-x-001" + }, + // Non-required parameters + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/<>/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "databases": { + "value": [ + { + "collation": "SQL_Latin1_General_CP1_CI_AS", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "licenseType": "LicenseIncluded", + "maxSizeBytes": 34359738368, + "name": "<>-az-sqldb-x-001", + "skuCapacity": 12, + "skuFamily": "Gen5", + "skuName": "BC_Gen5", + "skuTier": "BusinessCritical" + } + ] + }, + "firewallRules": { + "value": [ + { + "endIpAddress": "0.0.0.0", + "name": "AllowAllWindowsAzureIps", + "startIpAddress": "0.0.0.0" + } + ] + }, + "location": { + "value": "westeurope" + }, + "lock": { + "value": "CanNotDelete" + }, + "minimalTlsVersion": { + "value": "1.2" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net" + ] + }, + "service": "sqlServer", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "securityAlertPolicies": { + "value": [ + { + "emailAccountAdmins": true, + "name": "Default", + "state": "Enabled" + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "virtualNetworkRules": { + "value": [ + { + "ignoreMissingVnetServiceEndpoint": true, + "name": "newVnetRule1", + "virtualNetworkSubnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "vulnerabilityAssessmentsStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + } + } + } +} +``` + +
+

+ +

Example 3: Pe

+ +
+ +via Bicep module + +```bicep +resource kv1 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: 'adp-<>-az-kv-x-001' + scope: resourceGroup('<>','<>') +} + +module servers './Microsoft.Sql/servers/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Servers' + params: { + // Required parameters + name: '<>-az-sqlsrv-pe-001' + // Non-required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: kv1.getSecret('administratorLoginPassword') + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net' + ] + } + service: 'sqlServer' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-sqlsrv-pe-001" + }, + // Non-required parameters + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "reference": { + "keyVault": { + "id": "/subscriptions/<>/resourceGroups/<>/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-x-001" + }, + "secretName": "administratorLoginPassword" + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net" + ] + }, + "service": "sqlServer", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Sql/servers/securityAlertPolicies/deploy.bicep b/modules/Microsoft.Sql/servers/securityAlertPolicies/deploy.bicep new file mode 100644 index 0000000..3115a97 --- /dev/null +++ b/modules/Microsoft.Sql/servers/securityAlertPolicies/deploy.bicep @@ -0,0 +1,73 @@ +@description('Required. The name of the Security Alert Policy.') +param name string + +@description('Optional. Specifies an array of alerts that are disabled. Allowed values are: Sql_Injection, Sql_Injection_Vulnerability, Access_Anomaly, Data_Exfiltration, Unsafe_Action, Brute_Force.') +param disabledAlerts array = [] + +@description('Optional. Specifies that the alert is sent to the account administrators.') +param emailAccountAdmins bool = false + +@description('Optional. Specifies an array of email addresses to which the alert is sent.') +param emailAddresses array = [] + +@description('Optional. Specifies the number of days to keep in the Threat Detection audit logs.') +param retentionDays int = 0 + +@description('Optional. Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param state string = 'Disabled' + +@description('Optional. Specifies the identifier key of the Threat Detection audit storage account..') +@secure() +param storageAccountAccessKey string = '' + +@description('Optional. Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs.') +param storageEndpoint string = '' + +@description('Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment.') +param serverName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: serverName +} + +resource securityAlertPolicy 'Microsoft.Sql/servers/securityAlertPolicies@2022-02-01-preview' = { + name: name + parent: server + properties: { + disabledAlerts: disabledAlerts + emailAccountAdmins: emailAccountAdmins + emailAddresses: emailAddresses + retentionDays: retentionDays + state: state + storageAccountAccessKey: empty(storageAccountAccessKey) ? null : storageAccountAccessKey + storageEndpoint: empty(storageEndpoint) ? null : storageEndpoint + } +} + +@description('The name of the deployed security alert policy.') +output name string = securityAlertPolicy.name + +@description('The resource ID of the deployed security alert policy.') +output resourceId string = securityAlertPolicy.id + +@description('The resource group of the deployed security alert policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/servers/securityAlertPolicies/readme.md b/modules/Microsoft.Sql/servers/securityAlertPolicies/readme.md new file mode 100644 index 0000000..60cb233 --- /dev/null +++ b/modules/Microsoft.Sql/servers/securityAlertPolicies/readme.md @@ -0,0 +1,53 @@ +# SQL Server Security Alert Policy `[Microsoft.Sql/servers/securityAlertPolicies]` + +This module deploys an SQL Server Security Alert Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/servers/securityAlertPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/securityAlertPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Security Alert Policy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serverName` | string | The name of the parent SQL Server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `disabledAlerts` | array | `[]` | | Specifies an array of alerts that are disabled. Allowed values are: Sql_Injection, Sql_Injection_Vulnerability, Access_Anomaly, Data_Exfiltration, Unsafe_Action, Brute_Force. | +| `emailAccountAdmins` | bool | `False` | | Specifies that the alert is sent to the account administrators. | +| `emailAddresses` | array | `[]` | | Specifies an array of email addresses to which the alert is sent. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `retentionDays` | int | `0` | | Specifies the number of days to keep in the Threat Detection audit logs. | +| `state` | string | `'Disabled'` | `[Disabled, Enabled]` | Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database. | +| `storageAccountAccessKey` | secureString | `''` | | Specifies the identifier key of the Threat Detection audit storage account.. | +| `storageEndpoint` | string | `''` | | Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed security alert policy. | +| `resourceGroupName` | string | The resource group of the deployed security alert policy. | +| `resourceId` | string | The resource ID of the deployed security alert policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/securityAlertPolicies/version.json b/modules/Microsoft.Sql/servers/securityAlertPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Sql/servers/securityAlertPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/servers/version.json b/modules/Microsoft.Sql/servers/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Sql/servers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Sql/servers/virtualNetworkRules/deploy.bicep b/modules/Microsoft.Sql/servers/virtualNetworkRules/deploy.bicep new file mode 100644 index 0000000..30e573a --- /dev/null +++ b/modules/Microsoft.Sql/servers/virtualNetworkRules/deploy.bicep @@ -0,0 +1,48 @@ +@description('Required. The name of the Server Virtual Network Rule.') +param name string + +@description('Optional. Allow creating a firewall rule before the virtual network has vnet service endpoint enabled.') +param ignoreMissingVnetServiceEndpoint bool = false + +@description('Required. The resource ID of the virtual network subnet.') +param virtualNetworkSubnetId string + +@description('Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment.') +param serverName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: serverName +} + +resource virtualNetworkRule 'Microsoft.Sql/servers/virtualNetworkRules@2022-02-01-preview' = { + name: name + parent: server + properties: { + ignoreMissingVnetServiceEndpoint: ignoreMissingVnetServiceEndpoint + virtualNetworkSubnetId: virtualNetworkSubnetId + } +} + +@description('The name of the deployed virtual network rule.') +output name string = virtualNetworkRule.name + +@description('The resource ID of the deployed virtual network rule.') +output resourceId string = virtualNetworkRule.id + +@description('The resource group of the deployed virtual network rule.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/servers/virtualNetworkRules/readme.md b/modules/Microsoft.Sql/servers/virtualNetworkRules/readme.md new file mode 100644 index 0000000..532dc4c --- /dev/null +++ b/modules/Microsoft.Sql/servers/virtualNetworkRules/readme.md @@ -0,0 +1,48 @@ +# Sql Servers VirtualNetworkRules `[Microsoft.Sql/servers/virtualNetworkRules]` + +This module deploys a Sql Server Virtual Network Rule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/servers/virtualNetworkRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/virtualNetworkRules) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Server Virtual Network Rule. | +| `virtualNetworkSubnetId` | string | The resource ID of the virtual network subnet. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serverName` | string | The name of the parent SQL Server. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `ignoreMissingVnetServiceEndpoint` | bool | `False` | Allow creating a firewall rule before the virtual network has vnet service endpoint enabled. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed virtual network rule. | +| `resourceGroupName` | string | The resource group of the deployed virtual network rule. | +| `resourceId` | string | The resource ID of the deployed virtual network rule. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/virtualNetworkRules/version.json b/modules/Microsoft.Sql/servers/virtualNetworkRules/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Sql/servers/virtualNetworkRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/Microsoft.Sql/servers/vulnerabilityAssessments/deploy.bicep b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/deploy.bicep new file mode 100644 index 0000000..89a0f5f --- /dev/null +++ b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/deploy.bicep @@ -0,0 +1,59 @@ +@description('Required. The name of the vulnerability assessment.') +param name string + +@description('Required. The Name of SQL Server.') +param serverName string + +@description('Optional. Recurring scans state.') +param recurringScansIsEnabled bool = false + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param recurringScansEmailSubscriptionAdmins bool = false + +@description('Optional. Specifies an array of email addresses to which the scan notification is sent.') +param recurringScansEmails array = [] + +@description('Optional. A blob storage to hold the scan results.') +param vulnerabilityAssessmentsStorageAccountId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-9319755b-f697-4146-b966-4656e0b46cac-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-02-01-preview' existing = { + name: serverName +} + +resource vulnerabilityAssessment 'Microsoft.Sql/servers/vulnerabilityAssessments@2022-02-01-preview' = { + name: name + parent: server + properties: { + storageContainerPath: 'https://${last(split(vulnerabilityAssessmentsStorageAccountId, '/'))}.blob.${environment().suffixes.storage}/vulnerability-assessment/' + storageAccountAccessKey: listKeys(vulnerabilityAssessmentsStorageAccountId, '2019-06-01').keys[0].value + recurringScans: { + isEnabled: recurringScansIsEnabled + emailSubscriptionAdmins: recurringScansEmailSubscriptionAdmins + emails: recurringScansEmails + } + } +} + +@description('The name of the deployed vulnerability assessment.') +output name string = vulnerabilityAssessment.name + +@description('The resource ID of the deployed vulnerability assessment.') +output resourceId string = vulnerabilityAssessment.id + +@description('The resource group of the deployed vulnerability assessment.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/servers/vulnerabilityAssessments/readme.md b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/readme.md new file mode 100644 index 0000000..f48d001 --- /dev/null +++ b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/readme.md @@ -0,0 +1,46 @@ +# SQL Server Vulnerability Assessments `[Microsoft.Sql/servers/vulnerabilityAssessments]` + +This module deploys a vulnerability assessment for a SQL server. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/servers/vulnerabilityAssessments` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/vulnerabilityAssessments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the vulnerability assessment. | +| `serverName` | string | The Name of SQL Server. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `recurringScansEmails` | array | `[]` | Specifies an array of email addresses to which the scan notification is sent. | +| `recurringScansEmailSubscriptionAdmins` | bool | `False` | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| `recurringScansIsEnabled` | bool | `False` | Recurring scans state. | +| `vulnerabilityAssessmentsStorageAccountId` | string | `''` | A blob storage to hold the scan results. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed vulnerability assessment. | +| `resourceGroupName` | string | The resource group of the deployed vulnerability assessment. | +| `resourceId` | string | The resource ID of the deployed vulnerability assessment. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/vulnerabilityAssessments/version.json b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/version.json new file mode 100644 index 0000000..badc0a2 --- /dev/null +++ b/modules/Microsoft.Sql/servers/vulnerabilityAssessments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/modules/Microsoft.Storage/storageAccounts/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Storage/storageAccounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..c1a40ed --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,95 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + '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') + 'Reader and Data Access': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Storage Account Backup Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12') + 'Storage Blob Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + 'Storage Blob Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b') + 'Storage Blob Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1') + 'Storage Blob Delegator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a') + 'Storage File Data SMB Share Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb') + 'Storage File Data SMB Share Elevated Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7') + 'Storage File Data SMB Share Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314') + 'Storage Queue Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + 'Storage Queue Data Message Processor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed') + 'Storage Queue Data Message Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a') + 'Storage Queue Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925') + 'Storage Table Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + 'Storage Table Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(storageAccount.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: storageAccount +}] diff --git a/modules/Microsoft.Storage/storageAccounts/.test/encr.parameters.json b/modules/Microsoft.Storage/storageAccounts/.test/encr.parameters.json new file mode 100644 index 0000000..15974de --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.test/encr.parameters.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsaencr001" + }, + "storageAccountSku": { + "value": "Standard_LRS" + }, + "requireInfrastructureEncryption": { + "value": true + }, + "systemAssignedIdentity": { + "value": false + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "allowBlobPublicAccess": { + "value": false + }, + "blobServices": { + "value": { + "containers": [ + { + "name": "<>container", + "publicAccess": "None" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "blob", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Storage/storageAccounts/.test/min.parameters.json b/modules/Microsoft.Storage/storageAccounts/.test/min.parameters.json new file mode 100644 index 0000000..739a147 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.test/min.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsamin001" + }, + "allowBlobPublicAccess": { + "value": false + } + } +} diff --git a/modules/Microsoft.Storage/storageAccounts/.test/nfs.parameters.json b/modules/Microsoft.Storage/storageAccounts/.test/nfs.parameters.json new file mode 100644 index 0000000..5896711 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.test/nfs.parameters.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsanfs001" + }, + "storageAccountSku": { + "value": "Premium_LRS" + }, + "storageAccountKind": { + "value": "FileStorage" + }, + "allowBlobPublicAccess": { + "value": false + }, + "supportsHttpsTrafficOnly": { + "value": false + }, + "fileServices": { + "value": { + "shares": [ + { + "name": "nfsfileshare", + "enabledProtocols": "NFS" + } + ] + } + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Storage/storageAccounts/.test/parameters.json b/modules/Microsoft.Storage/storageAccounts/.test/parameters.json new file mode 100644 index 0000000..aa3aa14 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.test/parameters.json @@ -0,0 +1,209 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsax001" + }, + "storageAccountSku": { + "value": "Standard_LRS" + }, + "allowBlobPublicAccess": { + "value": false + }, + "requireInfrastructureEncryption": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "blob", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ] + } + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "table", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.table.core.windows.net" + ] + } + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "queue", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.queue.core.windows.net" + ] + } + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "file", + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net" + ] + } + } + ] + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "virtualNetworkRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "action": "Allow" + } + ], + "ipRules": [ + { + "action": "Allow", + "value": "1.1.1.1" + } + ] + } + }, + "blobServices": { + "value": { + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "containers": [ + { + "name": "avdscripts", + "publicAccess": "None", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "archivecontainer", + "publicAccess": "None", + "enableWORM": true, + "WORMRetention": 666, + "allowProtectedAppendWrites": false + } + ] + } + }, + "fileServices": { + "value": { + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "shares": [ + { + "name": "avdprofiles", + "shareQuota": "5120", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "avdprofiles2", + "shareQuota": "5120" + } + ] + } + }, + "tableServices": { + "value": { + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "tables": [ + "table1", + "table2" + ] + } + }, + "queueServices": { + "value": { + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "queues": [ + { + "name": "queue1", + "metadata": {}, + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + { + "name": "queue2", + "metadata": {} + } + ] + } + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Storage/storageAccounts/.test/v1.parameters.json b/modules/Microsoft.Storage/storageAccounts/.test/v1.parameters.json new file mode 100644 index 0000000..6891db2 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/.test/v1.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>azsav1001" + }, + "storageAccountKind": { + "value": "Storage" + }, + "allowBlobPublicAccess": { + "value": false + } + } +} diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..6277b39 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,86 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + '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') + 'Reader and Data Access': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Storage Account Backup Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12') + 'Storage Blob Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + 'Storage Blob Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b') + 'Storage Blob Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1') + 'Storage Blob Delegator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}/${split(resourceId, '/')[12]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(container.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: container +}] diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/deploy.bicep new file mode 100644 index 0000000..ac86988 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/deploy.bicep @@ -0,0 +1,93 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. Name of the blob service.') +param blobServicesName string = 'default' + +@description('Required. The name of the storage container to deploy.') +param name string + +@description('Optional. Name of the immutable policy.') +param immutabilityPolicyName string = 'default' + +@allowed([ + 'Container' + 'Blob' + 'None' +]) +@description('Optional. Specifies whether data in the container may be accessed publicly and the level of access.') +param publicAccess string = 'None' + +@description('Optional. Configure immutability policy.') +param immutabilityPolicyProperties object = {} + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName + + resource blobServices 'blobServices@2021-09-01' existing = { + name: blobServicesName + } +} + +resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' = { + name: name + parent: storageAccount::blobServices + properties: { + publicAccess: publicAccess + } +} + +module immutabilityPolicy 'immutabilityPolicies/deploy.bicep' = if (!empty(immutabilityPolicyProperties)) { + name: immutabilityPolicyName + params: { + storageAccountName: storageAccount.name + blobServicesName: storageAccount::blobServices.name + containerName: container.name + immutabilityPeriodSinceCreationInDays: contains(immutabilityPolicyProperties, 'immutabilityPeriodSinceCreationInDays') ? immutabilityPolicyProperties.immutabilityPeriodSinceCreationInDays : 365 + allowProtectedAppendWrites: contains(immutabilityPolicyProperties, 'allowProtectedAppendWrites') ? immutabilityPolicyProperties.allowProtectedAppendWrites : true + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module container_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: container.id + } +}] + +@description('The name of the deployed container.') +output name string = container.name + +@description('The resource ID of the deployed container.') +output resourceId string = container.id + +@description('The resource group of the deployed container.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/deploy.bicep new file mode 100644 index 0000000..40c0de4 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/deploy.bicep @@ -0,0 +1,63 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Conditional. The name of the parent blob service. Required if the template is used in a standalone deployment.') +param blobServicesName string = 'default' + +@description('Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment.') +param containerName string + +@description('Optional. Name of the immutable policy.') +param name string = 'default' + +@description('Optional. The immutability period for the blobs in the container since the policy creation, in days.') +param immutabilityPeriodSinceCreationInDays int = 365 + +@description('Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API.') +param allowProtectedAppendWrites bool = true + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName + + resource blobServices 'blobServices@2021-09-01' existing = { + name: blobServicesName + + resource container 'containers@2021-09-01' existing = { + name: containerName + } + } +} + +resource immutabilityPolicy 'Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies@2021-09-01' = { + name: name + parent: storageAccount::blobServices::container + properties: { + immutabilityPeriodSinceCreationInDays: immutabilityPeriodSinceCreationInDays + allowProtectedAppendWrites: allowProtectedAppendWrites + } +} + +@description('The name of the deployed immutability policy.') +output name string = immutabilityPolicy.name + +@description('The resource ID of the deployed immutability policy.') +output resourceId string = immutabilityPolicy.id + +@description('The resource group of the deployed immutability policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/readme.md b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/readme.md new file mode 100644 index 0000000..e2775f5 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/readme.md @@ -0,0 +1,46 @@ +# Blob Container Immutability Policy `[Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies]` + +This module deploys an Immutability Policy for a blob container + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `blobServicesName` | string | `'default'` | The name of the parent blob service. Required if the template is used in a standalone deployment. | +| `containerName` | string | | The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment. | +| `storageAccountName` | string | | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `allowProtectedAppendWrites` | bool | `True` | This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `immutabilityPeriodSinceCreationInDays` | int | `365` | The immutability period for the blobs in the container since the policy creation, in days. | +| `name` | string | `'default'` | Name of the immutable policy. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed immutability policy. | +| `resourceGroupName` | string | The resource group of the deployed immutability policy. | +| `resourceId` | string | The resource ID of the deployed immutability policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/version.json b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/readme.md b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/readme.md new file mode 100644 index 0000000..3928bf6 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/readme.md @@ -0,0 +1,112 @@ +# Storage Account Container `[Microsoft.Storage/storageAccounts/blobServices/containers]` + +This module deploys a blob container + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the storage container to deploy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `blobServicesName` | string | `'default'` | | Name of the blob service. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `immutabilityPolicyName` | string | `'default'` | | Name of the immutable policy. | +| `immutabilityPolicyProperties` | object | `{object}` | | Configure immutability policy. | +| `publicAccess` | string | `'None'` | `[Blob, Container, None]` | Specifies whether data in the container may be accessed publicly and the level of access. | +| `roleAssignments` | array | `[]` | | 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'. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed container. | +| `resourceGroupName` | string | The resource group of the deployed container. | +| `resourceId` | string | The resource ID of the deployed container. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/containers/version.json b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/containers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/blobServices/deploy.bicep new file mode 100644 index 0000000..925d3ed --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/deploy.bicep @@ -0,0 +1,145 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. The name of the blob service.') +param name string = 'default' + +@description('Optional. Indicates whether DeleteRetentionPolicy is enabled for the Blob service.') +param deleteRetentionPolicy bool = true + +@description('Optional. Indicates the number of days that the deleted blob should be retained. The minimum specified value can be 1 and the maximum value can be 365.') +param deleteRetentionPolicyDays int = 7 + +@description('Optional. Automatic Snapshot is enabled if set to true.') +param automaticSnapshotPolicyEnabled bool = false + +@description('Optional. Blob containers to create.') +param containers array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +]) +param diagnosticLogCategoriesToEnable array = [ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Transaction' +]) +param diagnosticMetricsToEnable array = [ + 'Transaction' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' = { + name: name + parent: storageAccount + properties: { + deleteRetentionPolicy: { + enabled: deleteRetentionPolicy + days: deleteRetentionPolicyDays + } + automaticSnapshotPolicyEnabled: automaticSnapshotPolicyEnabled + } +} + +resource blobServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: blobServices +} + +module blobServices_container 'containers/deploy.bicep' = [for (container, index) in containers: { + name: '${deployment().name}-Container-${index}' + params: { + storageAccountName: storageAccount.name + blobServicesName: blobServices.name + name: container.name + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + roleAssignments: contains(container, 'roleAssignments') ? container.roleAssignments : [] + immutabilityPolicyProperties: contains(container, 'immutabilityPolicyProperties') ? container.immutabilityPolicyProperties : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed blob service.') +output name string = blobServices.name + +@description('The resource ID of the deployed blob service.') +output resourceId string = blobServices.id + +@description('The name of the deployed blob service.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/readme.md b/modules/Microsoft.Storage/storageAccounts/blobServices/readme.md new file mode 100644 index 0000000..b853661 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/readme.md @@ -0,0 +1,58 @@ +# Storage Account blob services `[Microsoft.Storage/storageAccounts/blobServices]` + +This module can be used to deploy a blob service into a storage account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Storage/storageAccounts/blobServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `automaticSnapshotPolicyEnabled` | bool | `False` | | Automatic Snapshot is enabled if set to true. | +| `containers` | _[containers](containers/readme.md)_ array | `[]` | | Blob containers to create. | +| `deleteRetentionPolicy` | bool | `True` | | Indicates whether DeleteRetentionPolicy is enabled for the Blob service. | +| `deleteRetentionPolicyDays` | int | `7` | | Indicates the number of days that the deleted blob should be retained. The minimum specified value can be 1 and the maximum value can be 365. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[StorageDelete, StorageRead, StorageWrite]` | `[StorageDelete, StorageRead, StorageWrite]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Transaction]` | `[Transaction]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | | The name of the blob service. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed blob service. | +| `resourceGroupName` | string | The name of the deployed blob service. | +| `resourceId` | string | The resource ID of the deployed blob service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/blobServices/version.json b/modules/Microsoft.Storage/storageAccounts/blobServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/blobServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/deploy.bicep new file mode 100644 index 0000000..aeb7604 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/deploy.bicep @@ -0,0 +1,399 @@ +@maxLength(24) +@description('Required. Name of the Storage Account.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@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. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + 'Storage' + 'StorageV2' + 'BlobStorage' + 'FileStorage' + 'BlockBlobStorage' +]) +@description('Optional. Type of Storage Account to create.') +param storageAccountKind string = 'StorageV2' + +@allowed([ + 'Standard_LRS' + 'Standard_GRS' + 'Standard_RAGRS' + 'Standard_ZRS' + 'Premium_LRS' + 'Premium_ZRS' + 'Standard_GZRS' + 'Standard_RAGZRS' +]) +@description('Optional. Storage Account Sku Name.') +param storageAccountSku string = 'Standard_GRS' + +@allowed([ + 'Hot' + 'Cool' +]) +@description('Optional. Storage Account Access Tier.') +param storageAccountAccessTier string = 'Hot' + +@description('Optional. Provides the identity based authentication settings for Azure Files.') +param azureFilesIdentityBasedAuthentication object = {} + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. The Storage Account ManagementPolicies Rules.') +param managementPolicyRules array = [] + +@description('Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. For security reasons, it is recommended to set the DefaultAction Deny.') +param networkAcls object = {} + +@description('Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true.') +param requireInfrastructureEncryption bool = true + +@description('Optional. Blob service and containers to deploy.') +param blobServices object = {} + +@description('Optional. File service and shares to deploy.') +param fileServices object = {} + +@description('Optional. Queue service and queues to create.') +param queueServices object = {} + +@description('Optional. Table service and tables to create.') +param tableServices object = {} + +@description('Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false.') +param allowBlobPublicAccess bool = false + +@allowed([ + 'TLS1_0' + 'TLS1_1' + 'TLS1_2' +]) +@description('Optional. Set the minimum TLS version on request to storage.') +param minimumTlsVersion string = 'TLS1_2' + +@description('Optional. If true, enables Hierarchical Namespace for the storage account.') +param enableHierarchicalNamespace bool = false + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. Allows HTTPS traffic only to storage service if sets to true.') +param supportsHttpsTrafficOnly bool = true + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Transaction' +]) +param diagnosticMetricsToEnable array = [ + 'Transaction' +] + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter \'systemAssignedIdentity\' enabled.') +param cMKKeyName string = '' + +@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, latest is used.') +param cMKKeyVersion string = '' + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var supportsBlobService = storageAccountKind == 'BlockBlobStorage' || storageAccountKind == 'BlobStorage' || storageAccountKind == 'StorageV2' || storageAccountKind == 'Storage' +var supportsFileService = storageAccountKind == 'FileStorage' || storageAccountKind == 'StorageV2' || storageAccountKind == 'Storage' + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: name + location: location + kind: storageAccountKind + sku: { + name: storageAccountSku + } + identity: identity + tags: tags + properties: { + encryption: { + keySource: !empty(cMKKeyName) ? 'Microsoft.Keyvault' : 'Microsoft.Storage' + services: { + blob: supportsBlobService ? { + enabled: true + } : null + file: supportsFileService ? { + enabled: true + } : null + table: { + enabled: true + } + queue: { + enabled: true + } + } + requireInfrastructureEncryption: storageAccountKind != 'Storage' ? requireInfrastructureEncryption : null + keyvaultproperties: !empty(cMKKeyName) ? { + keyname: cMKKeyName + keyvaulturi: keyVault.properties.vaultUri + keyversion: !empty(cMKKeyVersion) ? cMKKeyVersion : null + } : null + identity: !empty(cMKKeyName) ? { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } : null + } + accessTier: storageAccountKind != 'Storage' ? storageAccountAccessTier : null + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + isHnsEnabled: enableHierarchicalNamespace ? enableHierarchicalNamespace : null + minimumTlsVersion: minimumTlsVersion + networkAcls: !empty(networkAcls) ? { + bypass: !empty(networkAcls) ? networkAcls.bypass : null + defaultAction: !empty(networkAcls) ? networkAcls.defaultAction : null + virtualNetworkRules: (!empty(networkAcls) && contains(networkAcls, 'virtualNetworkRules')) ? networkAcls.virtualNetworkRules : [] + ipRules: (!empty(networkAcls) && contains(networkAcls, 'ipRules')) ? networkAcls.ipRules : [] + } : null + allowBlobPublicAccess: allowBlobPublicAccess + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + azureFilesIdentityBasedAuthentication: !empty(azureFilesIdentityBasedAuthentication) ? azureFilesIdentityBasedAuthentication : null + } +} + +resource storageAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: storageAccount +} + +resource storageAccount_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${storageAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: storageAccount +} + +module storageAccount_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Storage-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: storageAccount.id + } +}] + +module storageAccount_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-StorageAccount-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(storageAccount.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: storageAccount.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +// Lifecycle Policy +module storageAccount_managementPolicies 'managementPolicies/deploy.bicep' = if (!empty(managementPolicyRules)) { + name: '${uniqueString(deployment().name, location)}-Storage-ManagementPolicies' + params: { + storageAccountName: storageAccount.name + rules: managementPolicyRules + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +// Containers +module storageAccount_blobServices 'blobServices/deploy.bicep' = if (!empty(blobServices)) { + name: '${uniqueString(deployment().name, location)}-Storage-BlobServices' + params: { + storageAccountName: storageAccount.name + containers: contains(blobServices, 'containers') ? blobServices.containers : [] + automaticSnapshotPolicyEnabled: contains(blobServices, 'automaticSnapshotPolicyEnabled') ? blobServices.automaticSnapshotPolicyEnabled : false + deleteRetentionPolicy: contains(blobServices, 'deleteRetentionPolicy') ? blobServices.deleteRetentionPolicy : true + deleteRetentionPolicyDays: contains(blobServices, 'deleteRetentionPolicyDays') ? blobServices.deleteRetentionPolicyDays : 7 + diagnosticLogsRetentionInDays: contains(blobServices, 'diagnosticLogsRetentionInDays') ? blobServices.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(blobServices, 'diagnosticStorageAccountId') ? blobServices.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(blobServices, 'diagnosticEventHubAuthorizationRuleId') ? blobServices.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(blobServices, 'diagnosticEventHubName') ? blobServices.diagnosticEventHubName : '' + diagnosticLogCategoriesToEnable: contains(blobServices, 'diagnosticLogCategoriesToEnable') ? blobServices.diagnosticLogCategoriesToEnable : [] + diagnosticMetricsToEnable: contains(blobServices, 'diagnosticMetricsToEnable') ? blobServices.diagnosticMetricsToEnable : [] + diagnosticWorkspaceId: contains(blobServices, 'diagnosticWorkspaceId') ? blobServices.diagnosticWorkspaceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +// File Shares +module storageAccount_fileServices 'fileServices/deploy.bicep' = if (!empty(fileServices)) { + name: '${uniqueString(deployment().name, location)}-Storage-FileServices' + params: { + storageAccountName: storageAccount.name + diagnosticLogsRetentionInDays: contains(fileServices, 'diagnosticLogsRetentionInDays') ? fileServices.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(fileServices, 'diagnosticStorageAccountId') ? fileServices.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(fileServices, 'diagnosticEventHubAuthorizationRuleId') ? fileServices.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(fileServices, 'diagnosticEventHubName') ? fileServices.diagnosticEventHubName : '' + diagnosticLogCategoriesToEnable: contains(fileServices, 'diagnosticLogCategoriesToEnable') ? fileServices.diagnosticLogCategoriesToEnable : [] + diagnosticMetricsToEnable: contains(fileServices, 'diagnosticMetricsToEnable') ? fileServices.diagnosticMetricsToEnable : [] + protocolSettings: contains(fileServices, 'protocolSettings') ? fileServices.protocolSettings : {} + shareDeleteRetentionPolicy: contains(fileServices, 'shareDeleteRetentionPolicy') ? fileServices.shareDeleteRetentionPolicy : { + enabled: true + days: 7 + } + shares: contains(fileServices, 'shares') ? fileServices.shares : [] + diagnosticWorkspaceId: contains(fileServices, 'diagnosticWorkspaceId') ? fileServices.diagnosticWorkspaceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +// Queue +module storageAccount_queueServices 'queueServices/deploy.bicep' = if (!empty(queueServices)) { + name: '${uniqueString(deployment().name, location)}-Storage-QueueServices' + params: { + storageAccountName: storageAccount.name + diagnosticLogsRetentionInDays: contains(queueServices, 'diagnosticLogsRetentionInDays') ? queueServices.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(queueServices, 'diagnosticStorageAccountId') ? queueServices.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(queueServices, 'diagnosticEventHubAuthorizationRuleId') ? queueServices.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(queueServices, 'diagnosticEventHubName') ? queueServices.diagnosticEventHubName : '' + diagnosticLogCategoriesToEnable: contains(queueServices, 'diagnosticLogCategoriesToEnable') ? queueServices.diagnosticLogCategoriesToEnable : [] + diagnosticMetricsToEnable: contains(queueServices, 'diagnosticMetricsToEnable') ? queueServices.diagnosticMetricsToEnable : [] + queues: contains(queueServices, 'queues') ? queueServices.queues : [] + diagnosticWorkspaceId: contains(queueServices, 'diagnosticWorkspaceId') ? queueServices.diagnosticWorkspaceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +// Table +module storageAccount_tableServices 'tableServices/deploy.bicep' = if (!empty(tableServices)) { + name: '${uniqueString(deployment().name, location)}-Storage-TableServices' + params: { + storageAccountName: storageAccount.name + diagnosticLogsRetentionInDays: contains(tableServices, 'diagnosticLogsRetentionInDays') ? tableServices.diagnosticLogsRetentionInDays : 365 + diagnosticStorageAccountId: contains(tableServices, 'diagnosticStorageAccountId') ? tableServices.diagnosticStorageAccountId : '' + diagnosticEventHubAuthorizationRuleId: contains(tableServices, 'diagnosticEventHubAuthorizationRuleId') ? tableServices.diagnosticEventHubAuthorizationRuleId : '' + diagnosticEventHubName: contains(tableServices, 'diagnosticEventHubName') ? tableServices.diagnosticEventHubName : '' + diagnosticLogCategoriesToEnable: contains(tableServices, 'diagnosticLogCategoriesToEnable') ? tableServices.diagnosticLogCategoriesToEnable : [] + diagnosticMetricsToEnable: contains(tableServices, 'diagnosticMetricsToEnable') ? tableServices.diagnosticMetricsToEnable : [] + tables: contains(tableServices, 'tables') ? tableServices.tables : [] + diagnosticWorkspaceId: contains(tableServices, 'diagnosticWorkspaceId') ? tableServices.diagnosticWorkspaceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The resource ID of the deployed storage account.') +output resourceId string = storageAccount.id + +@description('The name of the deployed storage account.') +output name string = storageAccount.name + +@description('The resource group of the deployed storage account.') +output resourceGroupName string = resourceGroup().name + +@description('The primary blob endpoint reference if blob services are deployed.') +output primaryBlobEndpoint string = !empty(blobServices) && contains(blobServices, 'containers') ? reference('Microsoft.Storage/storageAccounts/${storageAccount.name}', '2019-04-01').primaryEndpoints.blob : '' + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(storageAccount.identity, 'principalId') ? storageAccount.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = storageAccount.location diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/fileServices/deploy.bicep new file mode 100644 index 0000000..f70f8e6 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/deploy.bicep @@ -0,0 +1,143 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. The name of the file service.') +param name string = 'default' + +@description('Optional. Protocol settings for file service.') +param protocolSettings object = {} + +@description('Optional. The service properties for soft delete.') +param shareDeleteRetentionPolicy object = { + enabled: true + days: 7 +} + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. File shares to create.') +param shares array = [] + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +]) +param diagnosticLogCategoriesToEnable array = [ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Transaction' +]) +param diagnosticMetricsToEnable array = [ + 'Transaction' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +resource fileServices 'Microsoft.Storage/storageAccounts/fileServices@2021-09-01' = { + name: name + parent: storageAccount + properties: { + protocolSettings: protocolSettings + shareDeleteRetentionPolicy: shareDeleteRetentionPolicy + } +} + +resource fileServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: fileServices +} + +module fileServices_shares 'shares/deploy.bicep' = [for (share, index) in shares: { + name: '${deployment().name}-shares-${index}' + params: { + storageAccountName: storageAccount.name + fileServicesName: fileServices.name + name: share.name + enabledProtocols: contains(share, 'enabledProtocols') ? share.enabledProtocols : 'SMB' + rootSquash: contains(share, 'rootSquash') ? share.rootSquash : 'NoRootSquash' + sharedQuota: contains(share, 'sharedQuota') ? share.sharedQuota : 5120 + roleAssignments: contains(share, 'roleAssignments') ? share.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed file share service.') +output name string = fileServices.name + +@description('The resource ID of the deployed file share service.') +output resourceId string = fileServices.id + +@description('The resource group of the deployed file share service.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/readme.md b/modules/Microsoft.Storage/storageAccounts/fileServices/readme.md new file mode 100644 index 0000000..bafd75f --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/readme.md @@ -0,0 +1,56 @@ +# Storage Account file share services `[Microsoft.Storage/storageAccounts/fileServices]` + +This module can be used to deploy a file share service into a storage account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/fileServices/shares) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[StorageDelete, StorageRead, StorageWrite]` | `[StorageDelete, StorageRead, StorageWrite]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Transaction]` | `[Transaction]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | | The name of the file service. | +| `protocolSettings` | object | `{object}` | | Protocol settings for file service. | +| `shareDeleteRetentionPolicy` | object | `{object}` | | The service properties for soft delete. | +| `shares` | _[shares](shares/readme.md)_ array | `[]` | | File shares to create. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed file share service. | +| `resourceGroupName` | string | The resource group of the deployed file share service. | +| `resourceId` | string | The resource ID of the deployed file share service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/shares/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..6dfc280 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,95 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + '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') + 'Reader and Data Access': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Storage Account Backup Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12') + 'Storage Blob Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + 'Storage Blob Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b') + 'Storage Blob Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1') + 'Storage Blob Delegator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a') + 'Storage File Data SMB Share Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb') + 'Storage File Data SMB Share Elevated Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7') + 'Storage File Data SMB Share Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314') + 'Storage Queue Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + 'Storage Queue Data Message Processor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed') + 'Storage Queue Data Message Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a') + 'Storage Queue Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925') + 'Storage Table Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + 'Storage Table Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2019-06-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}/${split(resourceId, '/')[12]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(fileShare.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: fileShare +}] diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/shares/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/deploy.bicep new file mode 100644 index 0000000..aae763c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/deploy.bicep @@ -0,0 +1,85 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Conditional. The name of the parent file service. Required if the template is used in a standalone deployment.') +param fileServicesName string = 'default' + +@description('Required. The name of the file share to create.') +param name string + +@description('Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5TB (5120). For Large File Shares, the maximum size is 102400.') +param sharedQuota int = 5120 + +@allowed([ + 'NFS' + 'SMB' +]) +@description('Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share.') +param enabledProtocols string = 'SMB' + +@allowed([ + 'AllSquash' + 'NoRootSquash' + 'RootSquash' +]) +@description('Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares.') +param rootSquash string = 'NoRootSquash' + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName + + resource fileService 'fileServices@2021-09-01' existing = { + name: fileServicesName + } +} + +resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-09-01' = { + name: name + parent: storageAccount::fileService + properties: { + shareQuota: sharedQuota + rootSquash: enabledProtocols == 'NFS' ? rootSquash : null + enabledProtocols: enabledProtocols + } +} + +module fileShare_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: fileShare.id + } +}] + +@description('The name of the deployed file share.') +output name string = fileShare.name + +@description('The resource ID of the deployed file share.') +output resourceId string = fileShare.id + +@description('The resource group of the deployed file share.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/shares/readme.md b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/readme.md new file mode 100644 index 0000000..1401a44 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/readme.md @@ -0,0 +1,111 @@ +# File Share `[Microsoft.Storage/storageAccounts/fileServices/shares]` + +This module deploys a storage account file share. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/fileServices/shares) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the file share to create. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `fileServicesName` | string | `'default'` | The name of the parent file service. Required if the template is used in a standalone deployment. | +| `storageAccountName` | string | | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enabledProtocols` | string | `'SMB'` | `[NFS, SMB]` | The authentication protocol that is used for the file share. Can only be specified when creating a share. | +| `roleAssignments` | array | `[]` | | 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'. | +| `rootSquash` | string | `'NoRootSquash'` | `[AllSquash, NoRootSquash, RootSquash]` | Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares. | +| `sharedQuota` | int | `5120` | | The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5TB (5120). For Large File Shares, the maximum size is 102400. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed file share. | +| `resourceGroupName` | string | The resource group of the deployed file share. | +| `resourceId` | string | The resource ID of the deployed file share. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/shares/version.json b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/shares/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/fileServices/version.json b/modules/Microsoft.Storage/storageAccounts/fileServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/fileServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/managementPolicies/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/managementPolicies/deploy.bicep new file mode 100644 index 0000000..f88eed9 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/managementPolicies/deploy.bicep @@ -0,0 +1,48 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. The name of the storage container to deploy.') +param name string = 'default' + +@description('Required. The Storage Account ManagementPolicies Rules.') +param rules array + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +// lifecycle policy +resource managementPolicy 'Microsoft.Storage/storageAccounts/managementPolicies@2021-09-01' = if (!empty(rules)) { + name: name + parent: storageAccount + properties: { + policy: { + rules: rules + } + } +} + +@description('The resource ID of the deployed management policy.') +output resourceId string = managementPolicy.name + +@description('The name of the deployed management policy.') +output name string = managementPolicy.name + +@description('The resource group of the deployed management policy.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/managementPolicies/readme.md b/modules/Microsoft.Storage/storageAccounts/managementPolicies/readme.md new file mode 100644 index 0000000..5dd79a0 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/managementPolicies/readme.md @@ -0,0 +1,128 @@ +# Storage Account Management Policies `[Microsoft.Storage/storageAccounts/managementPolicies]` + +This module can be used to deploy a management policies into a storage account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Storage/storageAccounts/managementPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/managementPolicies) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `rules` | array | The Storage Account ManagementPolicies Rules. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | The name of the storage container to deploy. | + + +### Parameter Usage: `rules` + +

+ +Parameter JSON format + +```json +"rules": { + "value": [ + { + "enabled": true, + "name": "retention-policy", + "type": "Lifecycle", + "definition": { + "actions": { + "baseBlob": { + "tierToArchive": { + "daysAfterModificationGreaterThan": 30 + }, + "delete": { + "daysAfterModificationGreaterThan": 1096 + } + }, + "snapshot": { + "delete": { + "daysAfterCreationGreaterThan": 1096 + } + } + }, + "filters": { + "blobTypes": [ + "blockBlob" + ] + } + } + } + ] +} +``` +
+ + +
+ +Bicep format + +```bicep +rules: [ + { + enabled: true + name: 'retention-policy' + type: 'Lifecycle' + definition: { + actions: { + baseBlob: { + tierToArchive: { + daysAfterModificationGreaterThan: 30 + } + delete: { + daysAfterModificationGreaterThan: 1096 + } + } + snapshot: { + delete: { + daysAfterCreationGreaterThan: 1096 + } + } + } + filters: { + blobTypes: [ + 'blockBlob' + ] + } + } + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed management policy. | +| `resourceGroupName` | string | The resource group of the deployed management policy. | +| `resourceId` | string | The resource ID of the deployed management policy. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/managementPolicies/version.json b/modules/Microsoft.Storage/storageAccounts/managementPolicies/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/managementPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/queueServices/deploy.bicep new file mode 100644 index 0000000..34f4c05 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/deploy.bicep @@ -0,0 +1,129 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. The name of the queue service.') +param name string = 'default' + +@description('Optional. Queues to create.') +param queues array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +]) +param diagnosticLogCategoriesToEnable array = [ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Transaction' +]) +param diagnosticMetricsToEnable array = [ + 'Transaction' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +resource queueServices 'Microsoft.Storage/storageAccounts/queueServices@2021-09-01' = { + name: name + parent: storageAccount + properties: {} +} + +resource queueServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: queueServices +} + +module queueServices_queues 'queues/deploy.bicep' = [for (queue, index) in queues: { + name: '${deployment().name}-Queue-${index}' + params: { + storageAccountName: storageAccount.name + queueServicesName: queueServices.name + name: queue.name + metadata: contains(queue, 'metadata') ? queue.metadata : {} + roleAssignments: contains(queue, 'roleAssignments') ? queue.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed file share service.') +output name string = queueServices.name + +@description('The resource ID of the deployed file share service.') +output resourceId string = queueServices.id + +@description('The resource group of the deployed file share service.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/queues/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..a70afd1 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,92 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Avere Cluster Create': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7b1b19a-0e83-4fe5-935c-faaefbfd18c3') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Service Deploy Release Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21d96096-b162-414a-8302-d8354f9d91b2') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'CAL-Custom-Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b266cd7-0bba-4ae2-8423-90ede5e1e898') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce') + 'Dsms Role (deprecated)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b91f4c0b-46e3-47bb-a242-eecfe23b3b5b') + 'Dsms Role (do not use)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7aff565e-6c55-448d-83db-ccf482c6da2f') + 'GenevaWarmPathResourceContributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9f15f5f5-77bd-413a-aa88-4b9c68b1e7bc') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + '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') + 'masterreader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a48d7796-14b4-4889-afef-fbb65a93e5a2') + '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') + 'Reader and Data Access': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + 'Storage Account Backup Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12') + 'Storage Queue Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + 'Storage Queue Data Message Processor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed') + 'Storage Queue Data Message Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a') + 'Storage Queue Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') +} + +resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2021-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}/${split(resourceId, '/')[12]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(queue.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: queue +}] diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/queues/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/deploy.bicep new file mode 100644 index 0000000..fe8ce29 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/deploy.bicep @@ -0,0 +1,68 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Conditional. The name of the parent queue service. Required if the template is used in a standalone deployment.') +param queueServicesName string = 'default' + +@description('Required. The name of the storage queue to deploy.') +param name string + +@description('Required. A name-value pair that represents queue metadata.') +param metadata object = {} + +@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. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName + + resource queueServices 'queueServices@2021-09-01' existing = { + name: queueServicesName + } +} + +resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2021-09-01' = { + name: name + parent: storageAccount::queueServices + properties: { + metadata: metadata + } +} + +module queue_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: queue.id + } +}] + +@description('The name of the deployed queue.') +output name string = queue.name + +@description('The resource ID of the deployed queue.') +output resourceId string = queue.id + +@description('The resource group of the deployed queue.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/queues/readme.md b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/readme.md new file mode 100644 index 0000000..2ec1604 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/readme.md @@ -0,0 +1,109 @@ +# Storage Account Queue `[Microsoft.Storage/storageAccounts/queueServices/queues]` + +This module deploys a storage account queue + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/queueServices/queues) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `metadata` | object | A name-value pair that represents queue metadata. | +| `name` | string | The name of the storage queue to deploy. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `queueServicesName` | string | `'default'` | The name of the parent queue service. Required if the template is used in a standalone deployment. | +| `storageAccountName` | string | | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `roleAssignments` | array | `[]` | 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'. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed queue. | +| `resourceGroupName` | string | The resource group of the deployed queue. | +| `resourceId` | string | The resource ID of the deployed queue. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/queues/version.json b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/queues/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/readme.md b/modules/Microsoft.Storage/storageAccounts/queueServices/readme.md new file mode 100644 index 0000000..27f6851 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/readme.md @@ -0,0 +1,54 @@ +# Storage Account Queue Services `[Microsoft.Storage/storageAccounts/queueServices]` + +This module can be used to deploy a file share service into a storage account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/queueServices/queues) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[StorageDelete, StorageRead, StorageWrite]` | `[StorageDelete, StorageRead, StorageWrite]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Transaction]` | `[Transaction]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | | The name of the queue service. | +| `queues` | _[queues](queues/readme.md)_ array | `[]` | | Queues to create. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed file share service. | +| `resourceGroupName` | string | The resource group of the deployed file share service. | +| `resourceId` | string | The resource ID of the deployed file share service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/queueServices/version.json b/modules/Microsoft.Storage/storageAccounts/queueServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/queueServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/readme.md b/modules/Microsoft.Storage/storageAccounts/readme.md new file mode 100644 index 0000000..ee77a8c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/readme.md @@ -0,0 +1,1125 @@ +# Storage Accounts `[Microsoft.Storage/storageAccounts]` + +This module is used to deploy a storage account, with the ability to deploy 1 or more blob containers, file shares, tables and queues. Optional ACLs can be configured on the storage account and optional RBAC can be assigned on the storage account and on each child resource. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Storage/storageAccounts` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts) | +| `Microsoft.Storage/storageAccounts/blobServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/fileServices/shares) | +| `Microsoft.Storage/storageAccounts/managementPolicies` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/managementPolicies) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/tableServices/tables) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Storage Account. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cMKUserAssignedIdentityResourceId` | string | `''` | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowBlobPublicAccess` | bool | `False` | | Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false. | +| `azureFilesIdentityBasedAuthentication` | object | `{object}` | | Provides the identity based authentication settings for Azure Files. | +| `blobServices` | _[blobServices](blobServices/readme.md)_ object | `{object}` | | Blob service and containers to deploy. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter 'systemAssignedIdentity' enabled. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, latest is used. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Transaction]` | `[Transaction]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enableHierarchicalNamespace` | bool | `False` | | If true, enables Hierarchical Namespace for the storage account. | +| `fileServices` | _[fileServices](fileServices/readme.md)_ object | `{object}` | | File service and shares to deploy. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managementPolicyRules` | array | `[]` | | The Storage Account ManagementPolicies Rules. | +| `minimumTlsVersion` | string | `'TLS1_2'` | `[TLS1_0, TLS1_1, TLS1_2]` | Set the minimum TLS version on request to storage. | +| `networkAcls` | object | `{object}` | | Networks ACLs, this value contains IPs to whitelist and/or Subnet information. For security reasons, it is recommended to set the DefaultAction Deny. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. | +| `queueServices` | _[queueServices](queueServices/readme.md)_ object | `{object}` | | Queue service and queues to create. | +| `requireInfrastructureEncryption` | bool | `True` | | A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true. | +| `roleAssignments` | array | `[]` | | 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'. | +| `storageAccountAccessTier` | string | `'Hot'` | `[Cool, Hot]` | Storage Account Access Tier. | +| `storageAccountKind` | string | `'StorageV2'` | `[BlobStorage, BlockBlobStorage, FileStorage, Storage, StorageV2]` | Type of Storage Account to create. | +| `storageAccountSku` | string | `'Standard_GRS'` | `[Premium_LRS, Premium_ZRS, Standard_GRS, Standard_GZRS, Standard_LRS, Standard_RAGRS, Standard_RAGZRS, Standard_ZRS]` | Storage Account Sku Name. | +| `supportsHttpsTrafficOnly` | bool | `True` | | Allows HTTPS traffic only to storage service if sets to true. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tableServices` | _[tableServices](tableServices/readme.md)_ object | `{object}` | | Table service and tables to create. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `networkAcls` + +

+ +Parameter JSON format + +```json +"networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "virtualNetworkRules": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001" + } + ], + "ipRules": [ + { + "action": "Allow", + "value": "1.1.1.1" + } + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + } + ] + ipRules: [ + { + action: 'Allow' + value: '1.1.1.1' + } + ] +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed storage account. | +| `primaryBlobEndpoint` | string | The primary blob endpoint reference if blob services are deployed. | +| `resourceGroupName` | string | The resource group of the deployed storage account. | +| `resourceId` | string | The resource ID of the deployed storage account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Considerations + +This is a generic module for deploying a Storage Account. Any customization for different storage needs (such as a diagnostic or other storage account) need to be done through the Archetype. +The hierarchical namespace of the storage account (see parameter `enableHierarchicalNamespace`), can be only set at creation time. + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encr

+ +
+ +via Bicep module + +```bicep +module storageAccounts './Microsoft.Storage/storageAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StorageAccounts' + params: { + // Required parameters + name: '<>azsaencr001' + // Non-required parameters + allowBlobPublicAccess: false + blobServices: { + containers: [ + { + name: '<>container' + publicAccess: 'None' + } + ] + } + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net' + ] + } + service: 'blob' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + requireInfrastructureEncryption: true + storageAccountSku: 'Standard_LRS' + systemAssignedIdentity: false + userAssignedIdentities: { + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>azsaencr001" + }, + // Non-required parameters + "allowBlobPublicAccess": { + "value": false + }, + "blobServices": { + "value": { + "containers": [ + { + "name": "<>container", + "publicAccess": "None" + } + ] + } + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ] + }, + "service": "blob", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "requireInfrastructureEncryption": { + "value": true + }, + "storageAccountSku": { + "value": "Standard_LRS" + }, + "systemAssignedIdentity": { + "value": false + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module storageAccounts './Microsoft.Storage/storageAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StorageAccounts' + params: { + // Required parameters + name: '<>azsamin001' + // Non-required parameters + allowBlobPublicAccess: false + } +} +``` + +
+

+ +

+ +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": "<>azsamin001" + }, + // Non-required parameters + "allowBlobPublicAccess": { + "value": false + } + } +} +``` + +
+

+ +

Example 3: Nfs

+ +
+ +via Bicep module + +```bicep +module storageAccounts './Microsoft.Storage/storageAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StorageAccounts' + params: { + // Required parameters + name: '<>azsanfs001' + // Non-required parameters + allowBlobPublicAccess: false + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + fileServices: { + shares: [ + { + enabledProtocols: 'NFS' + name: 'nfsfileshare' + } + ] + } + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + storageAccountKind: 'FileStorage' + storageAccountSku: 'Premium_LRS' + supportsHttpsTrafficOnly: false + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>azsanfs001" + }, + // Non-required parameters + "allowBlobPublicAccess": { + "value": false + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "fileServices": { + "value": { + "shares": [ + { + "enabledProtocols": "NFS", + "name": "nfsfileshare" + } + ] + } + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "storageAccountKind": { + "value": "FileStorage" + }, + "storageAccountSku": { + "value": "Premium_LRS" + }, + "supportsHttpsTrafficOnly": { + "value": false + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 4: Parameters

+ +
+ +via Bicep module + +```bicep +module storageAccounts './Microsoft.Storage/storageAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StorageAccounts' + params: { + // Required parameters + name: '<>azsax001' + // Non-required parameters + allowBlobPublicAccess: false + blobServices: { + containers: [ + { + name: 'avdscripts' + publicAccess: 'None' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + { + allowProtectedAppendWrites: false + enableWORM: true + name: 'archivecontainer' + publicAccess: 'None' + WORMRetention: 666 + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + } + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + fileServices: { + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + shares: [ + { + name: 'avdprofiles' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + shareQuota: '5120' + } + { + name: 'avdprofiles2' + shareQuota: '5120' + } + ] + } + lock: 'CanNotDelete' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + ipRules: [ + { + action: 'Allow' + value: '1.1.1.1' + } + ] + virtualNetworkRules: [ + { + action: 'Allow' + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' + } + ] + } + privateEndpoints: [ + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net' + ] + } + service: 'blob' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.table.core.windows.net' + ] + } + service: 'table' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.queue.core.windows.net' + ] + } + service: 'queue' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + { + privateDnsZoneGroups: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net' + ] + } + service: 'file' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + queueServices: { + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + queues: [ + { + metadata: {} + name: 'queue1' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } + { + metadata: {} + name: 'queue2' + } + ] + } + requireInfrastructureEncryption: true + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + storageAccountSku: 'Standard_LRS' + systemAssignedIdentity: true + tableServices: { + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + tables: [ + 'table1' + 'table2' + ] + } + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>azsax001" + }, + // Non-required parameters + "allowBlobPublicAccess": { + "value": false + }, + "blobServices": { + "value": { + "containers": [ + { + "name": "avdscripts", + "publicAccess": "None", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + { + "allowProtectedAppendWrites": false, + "enableWORM": true, + "name": "archivecontainer", + "publicAccess": "None", + "WORMRetention": 666 + } + ], + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "fileServices": { + "value": { + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "shares": [ + { + "name": "avdprofiles", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ], + "shareQuota": "5120" + }, + { + "name": "avdprofiles2", + "shareQuota": "5120" + } + ] + } + }, + "lock": { + "value": "CanNotDelete" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "ipRules": [ + { + "action": "Allow", + "value": "1.1.1.1" + } + ], + "virtualNetworkRules": [ + { + "action": "Allow", + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" + } + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ] + }, + "service": "blob", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.table.core.windows.net" + ] + }, + "service": "table", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.queue.core.windows.net" + ] + }, + "service": "queue", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + }, + { + "privateDnsZoneGroups": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net" + ] + }, + "service": "file", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "queueServices": { + "value": { + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "queues": [ + { + "metadata": {}, + "name": "queue1", + "roleAssignments": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + { + "metadata": {}, + "name": "queue2" + } + ] + } + }, + "requireInfrastructureEncryption": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "storageAccountSku": { + "value": "Standard_LRS" + }, + "systemAssignedIdentity": { + "value": true + }, + "tableServices": { + "value": { + "diagnosticEventHubAuthorizationRuleId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey", + "diagnosticEventHubName": "adp-<>-az-evh-x-001", + "diagnosticLogsRetentionInDays": 7, + "diagnosticStorageAccountId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001", + "diagnosticWorkspaceId": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001", + "tables": [ + "table1", + "table2" + ] + } + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 5: V1

+ +
+ +via Bicep module + +```bicep +module storageAccounts './Microsoft.Storage/storageAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StorageAccounts' + params: { + // Required parameters + name: '<>azsav1001' + // Non-required parameters + allowBlobPublicAccess: false + storageAccountKind: 'Storage' + } +} +``` + +
+

+ +

+ +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": "<>azsav1001" + }, + // Non-required parameters + "allowBlobPublicAccess": { + "value": false + }, + "storageAccountKind": { + "value": "Storage" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/tableServices/deploy.bicep new file mode 100644 index 0000000..d362aa4 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/deploy.bicep @@ -0,0 +1,127 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Optional. The name of the table service.') +param name string = 'default' + +@description('Optional. tables to create.') +param tables array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +]) +param diagnosticLogCategoriesToEnable array = [ + 'StorageRead' + 'StorageWrite' + 'StorageDelete' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'Transaction' +]) +param diagnosticMetricsToEnable array = [ + 'Transaction' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +resource tableServices 'Microsoft.Storage/storageAccounts/tableServices@2021-09-01' = { + name: name + parent: storageAccount + properties: {} +} + +resource tableServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: tableServices +} + +module tableServices_tables 'tables/deploy.bicep' = [for (tableName, index) in tables: { + name: '${deployment().name}-Table-${index}' + params: { + storageAccountName: storageAccount.name + tableServicesName: tableServices.name + name: tableName + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed table service.') +output name string = tableServices.name + +@description('The resource ID of the deployed table service.') +output resourceId string = tableServices.id + +@description('The resource group of the deployed table service.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/readme.md b/modules/Microsoft.Storage/storageAccounts/tableServices/readme.md new file mode 100644 index 0000000..b1a1f90 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/readme.md @@ -0,0 +1,53 @@ +# Storage Account Table Services `[Microsoft.Storage/storageAccounts/tableServices]` + +This module deploys a storage account table service + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/tableServices/tables) | + +## Parameters + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountName` | string | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[StorageDelete, StorageRead, StorageWrite]` | `[StorageDelete, StorageRead, StorageWrite]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[Transaction]` | `[Transaction]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `name` | string | `'default'` | | The name of the table service. | +| `tables` | _[tables](tables/readme.md)_ array | `[]` | | tables to create. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed table service. | +| `resourceGroupName` | string | The resource group of the deployed table service. | +| `resourceId` | string | The resource ID of the deployed table service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/tables/deploy.bicep b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/deploy.bicep new file mode 100644 index 0000000..c7bce3e --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/deploy.bicep @@ -0,0 +1,46 @@ +@maxLength(24) +@description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') +param storageAccountName string + +@description('Conditional. The name of the parent table service. Required if the template is used in a standalone deployment.') +param tableServicesName string = 'default' + +@description('Required. Name of the table.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName + + resource tableServices 'tableServices@2021-09-01' existing = { + name: tableServicesName + } +} + +resource table 'Microsoft.Storage/storageAccounts/tableServices/tables@2021-09-01' = { + name: name + parent: storageAccount::tableServices +} + +@description('The name of the deployed file share service.') +output name string = table.name + +@description('The resource ID of the deployed file share service.') +output resourceId string = table.id + +@description('The resource group of the deployed file share service.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/tables/readme.md b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/readme.md new file mode 100644 index 0000000..f8f69b7 --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/readme.md @@ -0,0 +1,47 @@ +# Storage Account Table `[Microsoft.Storage/storageAccounts/tableServices/tables]` + +This module deploys a storage account table + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2021-09-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Storage/2021-09-01/storageAccounts/tableServices/tables) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the table. | + +**Conditional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `storageAccountName` | string | | The name of the parent Storage Account. Required if the template is used in a standalone deployment. | +| `tableServicesName` | string | `'default'` | The name of the parent table service. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed file share service. | +| `resourceGroupName` | string | The resource group of the deployed file share service. | +| `resourceId` | string | The resource ID of the deployed file share service. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/tables/version.json b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/tables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/tableServices/version.json b/modules/Microsoft.Storage/storageAccounts/tableServices/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/tableServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Storage/storageAccounts/version.json b/modules/Microsoft.Storage/storageAccounts/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Storage/storageAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Synapse/privateLinkHubs/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Synapse/privateLinkHubs/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d8144f3 --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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@2022-04-01' = [for principalId in principalIds: { + name: guid(privateLinkHub.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateLinkHub +}] diff --git a/modules/Microsoft.Synapse/privateLinkHubs/.test/min.parameters.json b/modules/Microsoft.Synapse/privateLinkHubs/.test/min.parameters.json new file mode 100644 index 0000000..d30613f --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/.test/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/modules/Microsoft.Synapse/privateLinkHubs/.test/parameters.json b/modules/Microsoft.Synapse/privateLinkHubs/.test/parameters.json new file mode 100644 index 0000000..258a8f8 --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/.test/parameters.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>synplhx001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", + "principalIds": [ + "<>" + ] + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "Web", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azuresynapse.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Synapse/privateLinkHubs/deploy.bicep b/modules/Microsoft.Synapse/privateLinkHubs/deploy.bicep new file mode 100644 index 0000000..1bfed5a --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/deploy.bicep @@ -0,0 +1,102 @@ +@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' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkHub 'Microsoft.Synapse/privateLinkHubs@2021-06-01' = { + name: name + location: location + tags: tags +} + +// Resource Lock +resource privateLinkHub_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${privateLinkHub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkHub +} + +// RBAC +module privateLinkHub_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${deployment().name}-rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateLinkHub.id + } +}] + +// Private Endpoints +module privateLinkHub_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-PrivateLinkHub-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(privateLinkHub.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: privateLinkHub.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The resource ID of the deployed Synapse Private Link Hub.') +output resourceId string = privateLinkHub.id + +@description('The name of the deployed Synapse Private Link Hub.') +output name string = privateLinkHub.name + +@description('The resource group of the deployed Synapse Private Link Hub.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = privateLinkHub.location diff --git a/modules/Microsoft.Synapse/privateLinkHubs/readme.md b/modules/Microsoft.Synapse/privateLinkHubs/readme.md new file mode 100644 index 0000000..319d608 --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/readme.md @@ -0,0 +1,380 @@ +# Azure Synapse Analytics `[Microsoft.Synapse/privateLinkHubs]` + +This module deploys Azure Synapse Analytics (private link hubs). + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Synapse/privateLinkHubs` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/privateLinkHubs) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Private Link Hub. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | The geo-location where the resource lives. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `roleAssignments` | array | `[]` | | 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}` | | 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed Synapse Private Link Hub. | +| `resourceGroupName` | string | The resource group of the deployed Synapse Private Link Hub. | +| `resourceId` | string | The resource ID of the deployed Synapse Private Link Hub. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module privateLinkHubs './Microsoft.Synapse/privateLinkHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkHubs' + params: { + name: '<>synplhmin001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>synplhmin001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module privateLinkHubs './Microsoft.Synapse/privateLinkHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-PrivateLinkHubs' + params: { + // Required parameters + name: '<>synplhx001' + // Non-required parameters + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azuresynapse.net' + ] + } + service: 'Web' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>synplhx001" + }, + // Non-required parameters + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azuresynapse.net" + ] + }, + "service": "Web", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + }, + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Synapse/privateLinkHubs/version.json b/modules/Microsoft.Synapse/privateLinkHubs/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Synapse/privateLinkHubs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Synapse/workspaces/.bicep/nested_cmkRbac.bicep b/modules/Microsoft.Synapse/workspaces/.bicep/nested_cmkRbac.bicep new file mode 100644 index 0000000..716a02a --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.bicep/nested_cmkRbac.bicep @@ -0,0 +1,40 @@ +param keyvaultName string +param workspaceIdentity string +param usesRbacAuthorization bool = false + +// Workspace encryption - Assign Workspace System Identity Keyvault Crypto Reader at Encryption Keyvault +resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: keyvaultName +} + +// Assign RBAC role Key Vault Crypto User +resource workspace_cmk_rbac 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (usesRbacAuthorization) { + name: '${workspaceIdentity}-cmk-rbac' + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + principalId: workspaceIdentity + principalType: 'ServicePrincipal' + } + scope: keyVault +} + +// Assign Acess Policy for Keys +resource workspace_cmk_accessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = if (!usesRbacAuthorization) { + name: 'add' + parent: keyVault + properties: { + accessPolicies: [ + { + permissions: { + keys: [ + 'wrapKey' + 'unwrapKey' + 'get' + ] + } + objectId: workspaceIdentity + tenantId: tenant().tenantId + } + ] + } +} diff --git a/modules/Microsoft.Synapse/workspaces/.bicep/nested_rbac.bicep b/modules/Microsoft.Synapse/workspaces/.bicep/nested_rbac.bicep new file mode 100644 index 0000000..5ac365c --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.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 workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(workspace.name, principalId, roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + } + scope: workspace +}] diff --git a/modules/Microsoft.Synapse/workspaces/.test/encryptionwsai.parameters.json b/modules/Microsoft.Synapse/workspaces/.test/encryptionwsai.parameters.json new file mode 100644 index 0000000..d826031 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.test/encryptionwsai.parameters.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-synws-encryptwsai-001" + }, + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + "encryption": { + "value": true + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKUserAssignedIdentityResourceId": { + "value": true + }, + "encryptionActivateWorkspace": { + "value": true + } + } +} diff --git a/modules/Microsoft.Synapse/workspaces/.test/encryptionwuai.parameters.json b/modules/Microsoft.Synapse/workspaces/.test/encryptionwuai.parameters.json new file mode 100644 index 0000000..2159659 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.test/encryptionwuai.parameters.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-synws-encryptwuai-001" + }, + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + "encryption": { + "value": true + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "encryptionUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + } + } +} diff --git a/modules/Microsoft.Synapse/workspaces/.test/managedvnet.parameters.json b/modules/Microsoft.Synapse/workspaces/.test/managedvnet.parameters.json new file mode 100644 index 0000000..f18470f --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.test/managedvnet.parameters.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-synws-managedvnet-001" + }, + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse002" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + "managedVirtualNetwork": { + "value": true + }, + "preventDataExfiltration": { + "value": true + }, + "allowedAadTenantIdsForLinking": { + "value": [ + "<>" + ] + } + } +} diff --git a/modules/Microsoft.Synapse/workspaces/.test/min.parameters.json b/modules/Microsoft.Synapse/workspaces/.test/min.parameters.json new file mode 100644 index 0000000..c104b16 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.test/min.parameters.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-synws-min-001" + }, + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + } + } +} diff --git a/modules/Microsoft.Synapse/workspaces/.test/parameters.json b/modules/Microsoft.Synapse/workspaces/.test/parameters.json new file mode 100644 index 0000000..8ec1824 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/.test/parameters.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-synws-x-001" + }, + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + "initialWorkspaceAdminObjectId": { + "value": "<>" + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "SQL", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.sql.azuresynapse.net" + ] + } + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "SynapseRbacOperations", + "GatewayApiRequests", + "BuiltinSqlReqsEnded", + "IntegrationPipelineRuns", + "IntegrationActivityRuns", + "IntegrationTriggerRuns" + ] + } + } +} diff --git a/modules/Microsoft.Synapse/workspaces/deploy.bicep b/modules/Microsoft.Synapse/workspaces/deploy.bicep new file mode 100644 index 0000000..4b9d99e --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/deploy.bicep @@ -0,0 +1,317 @@ +// Parameters +@maxLength(50) +@description('Required. The name of the Synapse Workspace.') +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 = {} + +@description('Optional. Enable or Disable AzureADOnlyAuthentication on All Workspace sub-resource.') +param azureADOnlyAuthentication bool = false + +@description('Optional. AAD object ID of initial workspace admin.') +param initialWorkspaceAdminObjectID string = '' + +@description('Required. Name of the default ADLS Gen2 storage account.') +param defaultDataLakeStorageAccountName string + +@description('Required. The default ADLS Gen2 file system.') +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. Double encryption using a customer-managed key.') +param encryption bool = false + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Optional. Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault.') +param cMKUserAssignedIdentityResourceId bool = false + +@description('Optional. The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault.') +param encryptionUserAssignedIdentity string = '' + +@description('Optional. Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace.') +param encryptionActivateWorkspace bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@maxLength(90) +@description('Optional. Workspace managed resource group. The resource group name uniquely identifies the resource group within the user subscriptionId. The resource group name must be no longer than 90 characters long, and must be alphanumeric characters (Char.IsLetterOrDigit()) and \'-\', \'_\', \'(\', \')\' and\'.\'. Note that the name cannot end with \'.\'.') +param managedResourceGroupName string = '' + +@description('Optional. Enable this to ensure that connection from your workspace to your data sources use Azure Private Links. You can create managed private endpoints to your data sources.') +param managedVirtualNetwork bool = false + +@description('Optional. Allowed AAD Tenant IDs For Linking.') +param allowedAadTenantIdsForLinking array = [] + +@description('Optional. Linked Access Check On Target Resource.') +param linkedAccessCheckOnTargetResource bool = false + +@description('Optional. Prevent Data Exfiltration.') +param preventDataExfiltration bool = false + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. Enable or Disable public network access to workspace.') +param publicNetworkAccess string = 'Enabled' + +@description('Optional. Purview Resource ID.') +param purviewResourceID string = '' + +@description('Required. Login for administrator access to the workspace\'s SQL pools.') +param sqlAdministratorLogin string + +@description('Optional. Password for administrator access to the workspace\'s SQL pools. If you don\'t provide a password, one will be automatically generated. You can change the password later.') +#disable-next-line secure-secrets-in-params // Not a secret +param sqlAdministratorLoginPassword string = '' + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'SynapseRbacOperations' + 'GatewayApiRequests' + 'BuiltinSqlReqsEnded' + 'IntegrationPipelineRuns' + 'IntegrationActivityRuns' + 'IntegrationTriggerRuns' +]) +param diagnosticLogCategoriesToEnable array = [ + 'SynapseRbacOperations' + 'GatewayApiRequests' + 'BuiltinSqlReqsEnded' + 'IntegrationPipelineRuns' + 'IntegrationActivityRuns' + 'IntegrationTriggerRuns' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +// Variables +var userAssignedIdentitiesUnion = union(userAssignedIdentities, !empty(encryptionUserAssignedIdentity) ? { + '${encryptionUserAssignedIdentity}': {} + } : {}) + +var identityType = !empty(userAssignedIdentitiesUnion) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentitiesUnion) ? userAssignedIdentitiesUnion : null +} + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${cMKKeyVault.name}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' = { + name: name + location: location + identity: identity + tags: tags + properties: { + azureADOnlyAuthentication: azureADOnlyAuthentication ? azureADOnlyAuthentication : null + cspWorkspaceAdminProperties: !empty(initialWorkspaceAdminObjectID) ? { + initialWorkspaceAdminObjectId: initialWorkspaceAdminObjectID + } : null + defaultDataLakeStorage: { + accountUrl: 'https://${defaultDataLakeStorageAccountName}.dfs.${environment().suffixes.storage}' + filesystem: defaultDataLakeStorageFilesystem + createManagedPrivateEndpoint: managedVirtualNetwork ? defaultDataLakeStorageCreateManagedPrivateEndpoint : null + } + encryption: encryption ? { + cmk: { + kekIdentity: { + userAssignedIdentity: !empty(encryptionUserAssignedIdentity) ? encryptionUserAssignedIdentity : null + useSystemAssignedIdentity: cMKUserAssignedIdentityResourceId ? true : false + } + key: { + keyVaultUrl: cMKKeyVaultKey.properties.keyUri + name: cMKKeyName + } + } + } : null + managedResourceGroupName: !empty(managedResourceGroupName) ? managedResourceGroupName : null + managedVirtualNetwork: managedVirtualNetwork ? 'default' : null + managedVirtualNetworkSettings: managedVirtualNetwork ? { + allowedAadTenantIdsForLinking: allowedAadTenantIdsForLinking + linkedAccessCheckOnTargetResource: linkedAccessCheckOnTargetResource + preventDataExfiltration: preventDataExfiltration + } : null + publicNetworkAccess: managedVirtualNetwork ? publicNetworkAccess : null + purviewConfiguration: !empty(purviewResourceID) ? { + purviewResourceId: purviewResourceID + } : null + sqlAdministratorLogin: sqlAdministratorLogin + sqlAdministratorLoginPassword: !empty(sqlAdministratorLoginPassword) ? sqlAdministratorLoginPassword : null + } +} + +// Workspace encryption with customer managed keys +// - Assign Synapse Workspace MSI access to encryption key +module workspace_cmk_rbac './.bicep/nested_cmkRbac.bicep' = if (encryptionActivateWorkspace) { + name: '${workspace.name}-cmk-rbac' + params: { + workspaceIdentity: workspace.identity.principalId + keyvaultName: !empty(cMKKeyVaultResourceId) ? cMKKeyVault.name : '' + usesRbacAuthorization: !empty(cMKKeyVaultResourceId) ? cMKKeyVault.properties.enableRbacAuthorization : true + } + scope: encryptionActivateWorkspace ? resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) : resourceGroup() +} + +// - Workspace encryption - Activate Workspace +module workspace_key './keys/deploy.bicep' = if (encryptionActivateWorkspace) { + name: '${workspace.name}-cmk-activation' + params: { + name: cMKKeyName + isActiveCMK: true + keyVaultResourceId: cMKKeyVaultResourceId + workspaceName: workspace.name + } + dependsOn: [ + workspace_cmk_rbac + ] +} + +// Resource Lock +resource workspace_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${workspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: workspace +} + +// RBAC +module workspace_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Workspace-Rbac-${index}' + params: { + principalIds: roleAssignment.principalIds + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: workspace.id + } +}] + +// Endpoints +module workspace_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-Workspace-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(workspace.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: workspace.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +// Diagnostics Settings +resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: workspace +} + +@description('The resource ID of the deployed Synapse Workspace.') +output resourceID string = workspace.id + +@description('The name of the deployed Synapse Workspace.') +output name string = workspace.name + +@description('The resource group of the deployed Synapse Workspace.') +output resourceGroupName string = resourceGroup().name + +@description('The workspace connectivity endpoints.') +output connectivityEndpoints object = workspace.properties.connectivityEndpoints + +@description('The location the resource was deployed into.') +output location string = workspace.location diff --git a/modules/Microsoft.Synapse/workspaces/keys/deploy.bicep b/modules/Microsoft.Synapse/workspaces/keys/deploy.bicep new file mode 100644 index 0000000..03eaf89 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/keys/deploy.bicep @@ -0,0 +1,56 @@ +@description('Required. Encryption key name.') +param name string + +@description('Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Optional. The geo-location where the resource lives.') +param location string = resourceGroup().location + +@description('Required. Used to activate the workspace after a customer managed key is provided.') +param isActiveCMK bool + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param keyVaultResourceId string = '' + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = false + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(keyVaultResourceId)) { + name: '${last(split(keyVaultResourceId, '/'))}/${name}' + scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4]) +} + +resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { + name: workspaceName +} + +resource key 'Microsoft.Synapse/workspaces/keys@2021-06-01' = { + name: name + parent: workspace + properties: { + isActiveCMK: isActiveCMK + keyVaultUrl: cMKKeyVaultKey.properties.keyUri + } +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +@description('The name of the deployed key.') +output name string = key.name + +@description('The resource ID of the deployed key.') +output resourceId string = key.id + +@description('The resource group of the deployed key.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Synapse/workspaces/keys/readme.md b/modules/Microsoft.Synapse/workspaces/keys/readme.md new file mode 100644 index 0000000..66188d8 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/keys/readme.md @@ -0,0 +1,54 @@ +# Synapse Workspaces Keys `[Microsoft.Synapse/workspaces/keys]` + +This module deploys a Synapse Workspaces Key. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Template references](#Template-references) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Synapse/workspaces/keys` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `isActiveCMK` | bool | Used to activate the workspace after a customer managed key is provided. | +| `name` | string | Encryption key name. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `workspaceName` | string | The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `False` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `keyVaultResourceId` | string | `''` | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `location` | string | `[resourceGroup().location]` | The geo-location where the resource lives. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed key. | +| `resourceGroupName` | string | The resource group of the deployed key. | +| `resourceId` | string | The resource ID of the deployed key. | + +## Template references + +- [Workspaces/Keys](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Synapse/workspaces/keys/version.json b/modules/Microsoft.Synapse/workspaces/keys/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/keys/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Synapse/workspaces/readme.md b/modules/Microsoft.Synapse/workspaces/readme.md new file mode 100644 index 0000000..e314d88 --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/readme.md @@ -0,0 +1,716 @@ +# Synapse Workspaces `[Microsoft.Synapse/workspaces]` + +This module deploys a Synapse Workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2021-06-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2021-06-01-preview/vaults/accessPolicies) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Synapse/workspaces` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces) | +| `Microsoft.Synapse/workspaces/keys` | [2021-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `defaultDataLakeStorageAccountName` | string | Name of the default ADLS Gen2 storage account. | +| `defaultDataLakeStorageFilesystem` | string | The default ADLS Gen2 file system. | +| `name` | string | The name of the Synapse Workspace. | +| `sqlAdministratorLogin` | string | Login for administrator access to the workspace's SQL pools. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowedAadTenantIdsForLinking` | array | `[]` | | Allowed AAD Tenant IDs For Linking. | +| `azureADOnlyAuthentication` | bool | `False` | | Enable or Disable AzureADOnlyAuthentication on All Workspace sub-resource. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKUserAssignedIdentityResourceId` | bool | `False` | | Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault. | +| `defaultDataLakeStorageCreateManagedPrivateEndpoint` | bool | `False` | | 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. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[BuiltinSqlReqsEnded, GatewayApiRequests, IntegrationActivityRuns, IntegrationPipelineRuns, IntegrationTriggerRuns, SynapseRbacOperations]` | `[BuiltinSqlReqsEnded, GatewayApiRequests, IntegrationActivityRuns, IntegrationPipelineRuns, IntegrationTriggerRuns, SynapseRbacOperations]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `encryption` | bool | `False` | | Double encryption using a customer-managed key. | +| `encryptionActivateWorkspace` | bool | `False` | | Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace. | +| `encryptionUserAssignedIdentity` | string | `''` | | The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault. | +| `initialWorkspaceAdminObjectID` | string | `''` | | AAD object ID of initial workspace admin. | +| `linkedAccessCheckOnTargetResource` | bool | `False` | | Linked Access Check On Target Resource. | +| `location` | string | `[resourceGroup().location]` | | The geo-location where the resource lives. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedResourceGroupName` | string | `''` | | Workspace managed resource group. The resource group name uniquely identifies the resource group within the user subscriptionId. The resource group name must be no longer than 90 characters long, and must be alphanumeric characters (Char.IsLetterOrDigit()) and '-', '_', '(', ')' and'.'. Note that the name cannot end with '.'. | +| `managedVirtualNetwork` | bool | `False` | | Enable this to ensure that connection from your workspace to your data sources use Azure Private Links. You can create managed private endpoints to your data sources. | +| `preventDataExfiltration` | bool | `False` | | Prevent Data Exfiltration. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `'Enabled'` | `[Disabled, Enabled]` | Enable or Disable public network access to workspace. | +| `purviewResourceID` | string | `''` | | Purview Resource ID. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sqlAdministratorLoginPassword` | string | `''` | | Password for administrator access to the workspace's SQL pools. If you don't provide a password, one will be automatically generated. You can change the password later. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `connectivityEndpoints` | object | The workspace connectivity endpoints. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed Synapse Workspace. | +| `resourceGroupName` | string | The resource group of the deployed Synapse Workspace. | +| `resourceID` | string | The resource ID of the deployed Synapse Workspace. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Encryptionwsai

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Synapse/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + defaultDataLakeStorageAccountName: 'adp<>azsasynapse001' + defaultDataLakeStorageFilesystem: 'synapsews' + name: '<>-az-synws-encryptwsai-001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + cMKUserAssignedIdentityResourceId: true + encryption: true + encryptionActivateWorkspace: true + } +} +``` + +
+

+ +

+ +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 + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "name": { + "value": "<>-az-synws-encryptwsai-001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "cMKUserAssignedIdentityResourceId": { + "value": true + }, + "encryption": { + "value": true + }, + "encryptionActivateWorkspace": { + "value": true + } + } +} +``` + +
+

+ +

Example 2: Encryptionwuai

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Synapse/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + defaultDataLakeStorageAccountName: 'adp<>azsasynapse001' + defaultDataLakeStorageFilesystem: 'synapsews' + name: '<>-az-synws-encryptwuai-001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + cMKKeyName: 'keyEncryptionKey' + cMKKeyVaultResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002' + encryption: true + encryptionUserAssignedIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' + } +} +``` + +
+

+ +

+ +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 + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "name": { + "value": "<>-az-synws-encryptwuai-001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "cMKKeyName": { + "value": "keyEncryptionKey" + }, + "cMKKeyVaultResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/adp-<>-az-kv-nopr-002" + }, + "encryption": { + "value": true + }, + "encryptionUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" + } + } +} +``` + +
+

+ +

Example 3: Managedvnet

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Synapse/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + defaultDataLakeStorageAccountName: 'adp<>azsasynapse002' + defaultDataLakeStorageFilesystem: 'synapsews' + name: '<>-az-synws-managedvnet-001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + allowedAadTenantIdsForLinking: [ + '<>' + ] + managedVirtualNetwork: true + preventDataExfiltration: true + } +} +``` + +
+

+ +

+ +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 + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse002" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "name": { + "value": "<>-az-synws-managedvnet-001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "allowedAadTenantIdsForLinking": { + "value": [ + "<>" + ] + }, + "managedVirtualNetwork": { + "value": true + }, + "preventDataExfiltration": { + "value": true + } + } +} +``` + +
+

+ +

Example 4: Min

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Synapse/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + defaultDataLakeStorageAccountName: 'adp<>azsasynapse001' + defaultDataLakeStorageFilesystem: 'synapsews' + name: '<>-az-synws-min-001' + sqlAdministratorLogin: 'synwsadmin' + } +} +``` + +
+

+ +

+ +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 + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "name": { + "value": "<>-az-synws-min-001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + } + } +} +``` + +
+

+ +

Example 5: Parameters

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.Synapse/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Workspaces' + params: { + // Required parameters + defaultDataLakeStorageAccountName: 'adp<>azsasynapse001' + defaultDataLakeStorageFilesystem: 'synapsews' + name: '<>-az-synws-x-001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogCategoriesToEnable: [ + 'BuiltinSqlReqsEnded' + 'GatewayApiRequests' + 'IntegrationActivityRuns' + 'IntegrationPipelineRuns' + 'IntegrationTriggerRuns' + 'SynapseRbacOperations' + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + initialWorkspaceAdminObjectId: '<>' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.sql.azuresynapse.net' + ] + } + service: 'SQL' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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 + "defaultDataLakeStorageAccountName": { + "value": "adp<>azsasynapse001" + }, + "defaultDataLakeStorageFilesystem": { + "value": "synapsews" + }, + "name": { + "value": "<>-az-synws-x-001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogCategoriesToEnable": { + "value": [ + "BuiltinSqlReqsEnded", + "GatewayApiRequests", + "IntegrationActivityRuns", + "IntegrationPipelineRuns", + "IntegrationTriggerRuns", + "SynapseRbacOperations" + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsalaw001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "initialWorkspaceAdminObjectId": { + "value": "<>" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.sql.azuresynapse.net" + ] + }, + "service": "SQL", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Synapse/workspaces/version.json b/modules/Microsoft.Synapse/workspaces/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Synapse/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.VirtualMachineImages/imageTemplates/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.VirtualMachineImages/imageTemplates/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..17d5424 --- /dev/null +++ b/modules/Microsoft.VirtualMachineImages/imageTemplates/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,68 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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 imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2020-02-14' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(imageTemplate.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: imageTemplate +}] diff --git a/modules/Microsoft.VirtualMachineImages/imageTemplates/.test/parameters.json b/modules/Microsoft.VirtualMachineImages/imageTemplates/.test/parameters.json new file mode 100644 index 0000000..df382ff --- /dev/null +++ b/modules/Microsoft.VirtualMachineImages/imageTemplates/.test/parameters.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-imgt-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "userMsiName": { + "value": "adp-<>-az-msi-x-001" + }, + "userMsiResourceGroup": { + "value": "validation-rg" + }, + "buildTimeoutInMinutes": { + "value": 0 + }, + "vmSize": { + "value": "Standard_D2s_v3" + }, + "osDiskSizeGB": { + "value": 127 + }, + "subnetId": { + "value": "" + }, + "imageSource": { + "value": { + "type": "PlatformImage", + "publisher": "MicrosoftWindowsDesktop", + "offer": "Windows-10", + "sku": "19h2-evd", + "version": "latest" + } + }, + "customizationSteps": { + "value": [ + { + "type": "WindowsRestart", + "restartTimeout": "30m" + } + ] + }, + "managedImageName": { + "value": "<>-az-mi-x-001" + }, + "unManagedImageName": { + "value": "<>-az-umi-x-001" + }, + "sigImageDefinitionId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/galleries/adp<>azsigweux001/images/adp-<>-az-imgd-x-001" + }, + "imageReplicationRegions": { + "value": [] + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.VirtualMachineImages/imageTemplates/deploy.bicep b/modules/Microsoft.VirtualMachineImages/imageTemplates/deploy.bicep new file mode 100644 index 0000000..573f3a2 --- /dev/null +++ b/modules/Microsoft.VirtualMachineImages/imageTemplates/deploy.bicep @@ -0,0 +1,196 @@ +@description('Required. Name prefix of the Image Template to be built by the Azure Image Builder service.') +param name string + +@description('Required. Name of the User Assigned Identity to be used to deploy Image Templates in Azure Image Builder.') +param userMsiName string + +@description('Optional. Resource group of the user assigned identity.') +param userMsiResourceGroup string = resourceGroup().name + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Image build timeout in minutes. Allowed values: 0-960. 0 means the default 240 minutes.') +@minValue(0) +@maxValue(960) +param buildTimeoutInMinutes int = 0 + +@description('Optional. Specifies the size for the VM.') +param vmSize string = 'Standard_D2s_v3' + +@description('Optional. Specifies the size of OS disk.') +param osDiskSizeGB int = 128 + +@description('Optional. Resource ID of an already existing subnet, e.g. \'/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/\'. If no value is provided, a new VNET will be created in the target Resource Group.') +param subnetId string = '' + +@description('Required. Image source definition in object format.') +param imageSource object + +@description('Required. Customization steps to be run when building the VM image.') +param customizationSteps array + +@description('Optional. Name of the managed image that will be created in the AIB resourcegroup.') +param managedImageName string = '' + +@description('Optional. Name of the unmanaged image that will be created in the AIB resourcegroup.') +param unManagedImageName string = '' + +@description('Optional. Resource ID of Shared Image Gallery to distribute image to, e.g.: /subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/.') +param sigImageDefinitionId string = '' + +@description('Optional. List of the regions the image produced by this solution should be stored in the Shared Image Gallery. When left empty, the deployment\'s location will be taken as a default value.') +param imageReplicationRegions array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Generated. Do not provide a value! This date value is used to generate a unique image template name.') +param baseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +var managedImageName_var = '${managedImageName}-${baseTime}' +var managedImageId = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Compute/images/${managedImageName_var}' +var imageReplicationRegions_var = empty(imageReplicationRegions) ? array(location) : imageReplicationRegions + +var managedImage = { + type: 'ManagedImage' + imageId: managedImageId + location: location + runOutputName: '${managedImageName_var}-ManagedImage' + artifactTags: { + sourceType: imageSource.type + sourcePublisher: contains(imageSource, 'publisher') ? imageSource.publisher : null + sourceOffer: contains(imageSource, 'offer') ? imageSource.offer : null + sourceSku: contains(imageSource, 'sku') ? imageSource.sku : null + sourceVersion: contains(imageSource, 'version') ? imageSource.version : null + sourceImageId: contains(imageSource, 'imageId') ? imageSource.imageId : null + sourceImageVersionID: contains(imageSource, 'imageVersionID') ? imageSource.imageVersionID : null + creationTime: baseTime + } +} +var conditionalManagedImage = empty(managedImageName) ? [] : array(managedImage) +var sharedImage = { + type: 'SharedImage' + galleryImageId: sigImageDefinitionId + runOutputName: !empty(sigImageDefinitionId) ? '${split(sigImageDefinitionId, '/')[10]}-SharedImage' : 'SharedImage' + artifactTags: { + sourceType: imageSource.type + sourcePublisher: contains(imageSource, 'publisher') ? imageSource.publisher : null + sourceOffer: contains(imageSource, 'offer') ? imageSource.offer : null + sourceSku: contains(imageSource, 'sku') ? imageSource.sku : null + sourceVersion: contains(imageSource, 'version') ? imageSource.version : null + sourceImageId: contains(imageSource, 'imageId') ? imageSource.imageId : null + sourceImageVersionID: contains(imageSource, 'imageVersionID') ? imageSource.imageVersionID : null + creationTime: baseTime + } + replicationRegions: imageReplicationRegions_var +} +var conditionalSharedImage = empty(sigImageDefinitionId) ? [] : array(sharedImage) +var unManagedImage = { + type: 'VHD' + runOutputName: '${unManagedImageName}-VHD' + artifactTags: { + sourceType: imageSource.type + sourcePublisher: contains(imageSource, 'publisher') ? imageSource.publisher : null + sourceOffer: contains(imageSource, 'offer') ? imageSource.offer : null + sourceSku: contains(imageSource, 'sku') ? imageSource.sku : null + sourceVersion: contains(imageSource, 'version') ? imageSource.version : null + sourceImageId: contains(imageSource, 'imageId') ? imageSource.imageId : null + sourceImageVersionID: contains(imageSource, 'imageVersionID') ? imageSource.imageVersionID : null + creationTime: baseTime + } +} +var conditionalUnManagedImage = empty(unManagedImageName) ? [] : array(unManagedImage) +var distribute = concat(conditionalManagedImage, conditionalSharedImage, conditionalUnManagedImage) +var vnetConfig = { + subnetId: subnetId +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2020-02-14' = { + name: '${name}-${baseTime}' + location: location + tags: tags + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${az.resourceId(userMsiResourceGroup, 'Microsoft.ManagedIdentity/userAssignedIdentities', userMsiName)}': {} + } + } + properties: { + buildTimeoutInMinutes: buildTimeoutInMinutes + vmProfile: { + vmSize: vmSize + osDiskSizeGB: osDiskSizeGB + vnetConfig: !empty(subnetId) ? vnetConfig : null + } + source: imageSource + customize: customizationSteps + distribute: distribute + } +} + +resource imageTemplate_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${imageTemplate.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: imageTemplate +} + +module imageTemplate_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ImageTemplate-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: imageTemplate.id + } +}] + +@description('The resource ID of the image template.') +output resourceId string = imageTemplate.id + +@description('The resource group the image template was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The full name of the deployed image template.') +output name string = imageTemplate.name + +@description('The prefix of the image template name provided as input.') +output namePrefix string = name + +@description('The command to run in order to trigger the image build.') +output runThisCommand string = 'Invoke-AzResourceAction -ResourceName ${imageTemplate.name} -ResourceGroupName ${resourceGroup().name} -ResourceType Microsoft.VirtualMachineImages/imageTemplates -Action Run -Force' + +@description('The location the resource was deployed into.') +output location string = imageTemplate.location diff --git a/modules/Microsoft.VirtualMachineImages/imageTemplates/readme.md b/modules/Microsoft.VirtualMachineImages/imageTemplates/readme.md new file mode 100644 index 0000000..de58360 --- /dev/null +++ b/modules/Microsoft.VirtualMachineImages/imageTemplates/readme.md @@ -0,0 +1,405 @@ +# Image Templates `[Microsoft.VirtualMachineImages/imageTemplates]` + +This module deploys an image template that can be consumed by the Azure Image Builder (AIB) service. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.VirtualMachineImages/imageTemplates` | [2020-02-14](https://docs.microsoft.com/en-us/azure/templates/Microsoft.VirtualMachineImages/2020-02-14/imageTemplates) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `customizationSteps` | array | Customization steps to be run when building the VM image. | +| `imageSource` | object | Image source definition in object format. | +| `name` | string | Name prefix of the Image Template to be built by the Azure Image Builder service. | +| `userMsiName` | string | Name of the User Assigned Identity to be used to deploy Image Templates in Azure Image Builder. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `buildTimeoutInMinutes` | int | `0` | | Image build timeout in minutes. Allowed values: 0-960. 0 means the default 240 minutes. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `imageReplicationRegions` | array | `[]` | | List of the regions the image produced by this solution should be stored in the Shared Image Gallery. When left empty, the deployment's location will be taken as a default value. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedImageName` | string | `''` | | Name of the managed image that will be created in the AIB resourcegroup. | +| `osDiskSizeGB` | int | `128` | | Specifies the size of OS disk. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sigImageDefinitionId` | string | `''` | | Resource ID of Shared Image Gallery to distribute image to, e.g.: /subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/. | +| `subnetId` | string | `''` | | Resource ID of an already existing subnet, e.g. '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/'. If no value is provided, a new VNET will be created in the target Resource Group. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `unManagedImageName` | string | `''` | | Name of the unmanaged image that will be created in the AIB resourcegroup. | +| `userMsiResourceGroup` | string | `[resourceGroup().name]` | | Resource group of the user assigned identity. | +| `vmSize` | string | `'Standard_D2s_v3'` | | Specifies the size for the VM. | + +**Generated parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('yyyy-MM-dd-HH-mm-ss')]` | Do not provide a value! This date value is used to generate a unique image template name. | + + +### Parameter Usage: `imageSource` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +#### Platform Image + +

+ +Parameter JSON format + +```json +"source": { + "type": "PlatformImage", + "publisher": "MicrosoftWindowsDesktop", + "offer": "Windows-10", + "sku": "19h2-evd", + "version": "latest" +} +``` + +
+ +
+ +Bicep format + +```bicep +source: { + type: 'PlatformImage' + publisher: 'MicrosoftWindowsDesktop' + offer: 'Windows-10' + sku: '19h2-evd' + version: 'latest' +} +``` + +
+

+ +#### Managed Image + +

+ +Parameter JSON format + +```json +"source": { + "type": "ManagedImage", + "imageId": "/subscriptions//resourceGroups/{destinationResourceGroupName}/providers/Microsoft.Compute/images/" +} +``` + +
+ +
+ +Bicep format + +```bicep +source: { + type: 'ManagedImage' + imageId: '/subscriptions//resourceGroups/{destinationResourceGroupName}/providers/Microsoft.Compute/images/' +} +``` + +
+

+ +#### Shared Image + +

+ +Parameter JSON format + +```json +"source": { + "type": "SharedImageVersion", + "imageVersionID": "/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/" +} +``` + +
+ +
+ +Bicep format + +```bicep +source: { + type: 'SharedImageVersion' + imageVersionID: '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/' +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The full name of the deployed image template. | +| `namePrefix` | string | The prefix of the image template name provided as input. | +| `resourceGroupName` | string | The resource group the image template was deployed into. | +| `resourceId` | string | The resource ID of the image template. | +| `runThisCommand` | string | The command to run in order to trigger the image build. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module imageTemplates './Microsoft.VirtualMachineImages/imageTemplates/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-ImageTemplates' + params: { + // Required parameters + customizationSteps: [ + { + restartTimeout: '30m' + type: 'WindowsRestart' + } + ] + imageSource: { + offer: 'Windows-10' + publisher: 'MicrosoftWindowsDesktop' + sku: '19h2-evd' + type: 'PlatformImage' + version: 'latest' + } + name: '<>-az-imgt-x-001' + userMsiName: 'adp-<>-az-msi-x-001' + // Non-required parameters + buildTimeoutInMinutes: 0 + imageReplicationRegions: [] + lock: 'CanNotDelete' + managedImageName: '<>-az-mi-x-001' + osDiskSizeGB: 127 + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sigImageDefinitionId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/galleries/adp<>azsigweux001/images/adp-<>-az-imgd-x-001' + subnetId: '' + unManagedImageName: '<>-az-umi-x-001' + userMsiResourceGroup: 'validation-rg' + vmSize: 'Standard_D2s_v3' + } +} +``` + +
+

+ +

+ +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 + "customizationSteps": { + "value": [ + { + "restartTimeout": "30m", + "type": "WindowsRestart" + } + ] + }, + "imageSource": { + "value": { + "offer": "Windows-10", + "publisher": "MicrosoftWindowsDesktop", + "sku": "19h2-evd", + "type": "PlatformImage", + "version": "latest" + } + }, + "name": { + "value": "<>-az-imgt-x-001" + }, + "userMsiName": { + "value": "adp-<>-az-msi-x-001" + }, + // Non-required parameters + "buildTimeoutInMinutes": { + "value": 0 + }, + "imageReplicationRegions": { + "value": [] + }, + "lock": { + "value": "CanNotDelete" + }, + "managedImageName": { + "value": "<>-az-mi-x-001" + }, + "osDiskSizeGB": { + "value": 127 + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sigImageDefinitionId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Compute/galleries/adp<>azsigweux001/images/adp-<>-az-imgd-x-001" + }, + "subnetId": { + "value": "" + }, + "unManagedImageName": { + "value": "<>-az-umi-x-001" + }, + "userMsiResourceGroup": { + "value": "validation-rg" + }, + "vmSize": { + "value": "Standard_D2s_v3" + } + } +} +``` + +
+

diff --git a/modules/Microsoft.VirtualMachineImages/imageTemplates/version.json b/modules/Microsoft.VirtualMachineImages/imageTemplates/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.VirtualMachineImages/imageTemplates/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/connections/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Web/connections/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..0c67e56 --- /dev/null +++ b/modules/Microsoft.Web/connections/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + '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 connection 'Microsoft.Web/connections@2016-06-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(connection.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: connection +}] diff --git a/modules/Microsoft.Web/connections/.test/parameters.json b/modules/Microsoft.Web/connections/.test/parameters.json new file mode 100644 index 0000000..c286259 --- /dev/null +++ b/modules/Microsoft.Web/connections/.test/parameters.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "azuremonitor" + }, + "lock": { + "value": "CanNotDelete" + }, + "displayName": { + "value": "azuremonitorlogs" + }, + "connectionApi": { + "value": { + "id": "/subscriptions/<>/providers/Microsoft.Web/locations/westeurope/managedApis/azuremonitorlogs" + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Web/connections/deploy.bicep b/modules/Microsoft.Web/connections/deploy.bicep new file mode 100644 index 0000000..13a986f --- /dev/null +++ b/modules/Microsoft.Web/connections/deploy.bicep @@ -0,0 +1,109 @@ +@description('Optional. Alternative parameter values.') +param alternativeParameterValues object = {} + +@description('Optional. Specific values for some API connections.') +param connectionApi object = {} + +@description('Required. Connection name for connection. Example: \'azureblob\' when using blobs. It can change depending on the resource.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Customized parameter values for specific connections.') +param customParameterValues object = {} + +@description('Required. Display name connection. Example: \'blobconnection\' when using blobs. It can change depending on the resource.') +param displayName string + +@description('Optional. Location of the deployment.') +param location string = resourceGroup().location + +@description('Optional. Dictionary of nonsecret parameter values.') +#disable-next-line secure-secrets-in-params // Not a secret +param nonSecretParameterValues object = {} + +@description('Optional. Connection strings or access keys for connection. Example: \'accountName\' and \'accessKey\' when using blobs. It can change depending on the resource.') +@secure() +param parameterValues object = {} + +@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. Status of the connection.') +param statuses array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Links to test the API connection.') +param testLinks array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: name + location: location + tags: tags + properties: { + displayName: displayName + customParameterValues: customParameterValues + api: connectionApi + parameterValues: empty(alternativeParameterValues) ? parameterValues : null + nonSecretParameterValues: !empty(nonSecretParameterValues) ? nonSecretParameterValues : null + testLinks: !empty(testLinks) ? testLinks : null + statuses: !empty(statuses) ? statuses : null + } +} + +resource connection_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${connection.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: connection +} + +module connection_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Connection-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: connection.id + } +}] + +@description('The resource ID of the connection.') +output resourceId string = connection.id + +@description('The resource group the connection was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the connection.') +output name string = connection.name + +@description('The location the resource was deployed into.') +output location string = connection.location diff --git a/modules/Microsoft.Web/connections/readme.md b/modules/Microsoft.Web/connections/readme.md new file mode 100644 index 0000000..9a25bff --- /dev/null +++ b/modules/Microsoft.Web/connections/readme.md @@ -0,0 +1,239 @@ +# API Connections `[Microsoft.Web/connections]` + +This module deploys an Azure API connection. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Web/connections` | [2016-06-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/2016-06-01/connections) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `displayName` | string | Display name connection. Example: 'blobconnection' when using blobs. It can change depending on the resource. | +| `name` | string | Connection name for connection. Example: 'azureblob' when using blobs. It can change depending on the resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `alternativeParameterValues` | object | `{object}` | | Alternative parameter values. | +| `connectionApi` | object | `{object}` | | Specific values for some API connections. | +| `customParameterValues` | object | `{object}` | | Customized parameter values for specific connections. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location of the deployment. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `nonSecretParameterValues` | object | `{object}` | | Dictionary of nonsecret parameter values. | +| `parameterValues` | secureObject | `{object}` | | Connection strings or access keys for connection. Example: 'accountName' and 'accessKey' when using blobs. It can change depending on the resource. | +| `roleAssignments` | array | `[]` | | 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'. | +| `statuses` | array | `[]` | | Status of the connection. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `testLinks` | array | `[]` | | Links to test the API connection. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the connection. | +| `resourceGroupName` | string | The resource group the connection was deployed into. | +| `resourceId` | string | The resource ID of the connection. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module connections './Microsoft.Web/connections/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Connections' + params: { + // Required parameters + displayName: 'azuremonitorlogs' + name: 'azuremonitor' + // Non-required parameters + connectionApi: { + id: '/subscriptions/<>/providers/Microsoft.Web/locations/westeurope/managedApis/azuremonitorlogs' + } + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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 + "displayName": { + "value": "azuremonitorlogs" + }, + "name": { + "value": "azuremonitor" + }, + // Non-required parameters + "connectionApi": { + "value": { + "id": "/subscriptions/<>/providers/Microsoft.Web/locations/westeurope/managedApis/azuremonitorlogs" + } + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Web/connections/version.json b/modules/Microsoft.Web/connections/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/connections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/hostingEnvironments/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Web/hostingEnvironments/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..d645ca5 --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,69 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') +} + +resource appServiceEnvironment 'Microsoft.Web/hostingEnvironments@2021-02-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appServiceEnvironment.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appServiceEnvironment +}] diff --git a/modules/Microsoft.Web/hostingEnvironments/.test/asev2.parameters.json b/modules/Microsoft.Web/hostingEnvironments/.test/asev2.parameters.json new file mode 100644 index 0000000..c556495 --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/.test/asev2.parameters.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appse-asev2-001" + }, + "kind": { + "value": "ASEv2" + }, + "multiSize": { + "value": "Standard_D1_V2" + }, + "ipsslAddressCount": { + "value": 2 + }, + "clusterSettings": { + "value": [ + { + "name": "DisableTls1.0", + "value": "1" + } + ] + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-008" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Web/hostingEnvironments/.test/asev3.parameters.json b/modules/Microsoft.Web/hostingEnvironments/.test/asev3.parameters.json new file mode 100644 index 0000000..a304822 --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/.test/asev3.parameters.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-appse-asev3-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-006" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "clusterSettings": { + "value": [ + { + "name": "DisableTls1.0", + "value": "1" + } + ] + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + } + } +} diff --git a/modules/Microsoft.Web/hostingEnvironments/deploy.bicep b/modules/Microsoft.Web/hostingEnvironments/deploy.bicep new file mode 100644 index 0000000..c48db68 --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/deploy.bicep @@ -0,0 +1,196 @@ +@description('Required. Name of the App Service Environment.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Kind of resource.') +param kind string = 'ASEv3' + +@description('Required. ResourceId for the subnet.') +param subnetResourceId string + +@description('Optional. Specifies which endpoints to serve internally in the Virtual Network for the App Service Environment. - None, Web, Publishing, Web,Publishing.') +@allowed([ + 'None' + 'Web' + 'Publishing' +]) +param internalLoadBalancingMode string = 'None' + +@description('Optional. Frontend VM size. Cannot be used with \'kind\' `ASEv3`.') +@allowed([ + '' + 'Medium' + 'Large' + 'ExtraLarge' + 'Standard_D2' + 'Standard_D3' + 'Standard_D4' + 'Standard_D1_V2' + 'Standard_D2_V2' + 'Standard_D3_V2' + 'Standard_D4_V2' +]) +param multiSize string = '' + +@description('Optional. Number of IP SSL addresses reserved for the App Service Environment.') +param ipsslAddressCount int = -1 + +@description('Optional. DNS suffix of the App Service Environment.') +param dnsSuffix string = '' + +@description('Optional. Scale factor for frontends.') +param frontEndScaleFactor int = 15 + +@description('Optional. User added IP ranges to whitelist on ASE DB. Cannot be used with \'kind\' `ASEv3`.') +param userWhitelistedIpRanges array = [] + +@description('Optional. Custom settings for changing the behavior of the App Service Environment.') +param clusterSettings array = [ + { + name: 'DisableTls1.0' + value: '1' + } +] + +@description('Optional. Switch to make the App Service Environment zone redundant. If enabled, the minimum App Service plan instance count will be three, otherwise 1. If enabled, the `dedicatedHostCount` must be set to `-1`.') +param zoneRedundant bool = false + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The Dedicated Host Count. Is not supported by ASEv2. If `zoneRedundant` is false, and you want physical hardware isolation enabled, set to 2. Otherwise 0.') +param dedicatedHostCount int = -1 + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'AppServiceEnvironmentPlatformLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'AppServiceEnvironmentPlatformLogs' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appServiceEnvironment 'Microsoft.Web/hostingEnvironments@2021-03-01' = { + name: name + kind: kind + location: location + tags: tags + properties: { + virtualNetwork: { + id: subnetResourceId + subnet: last(split(subnetResourceId, '/')) + } + internalLoadBalancingMode: internalLoadBalancingMode + multiSize: !empty(multiSize) ? any(multiSize) : null + ipsslAddressCount: ipsslAddressCount != -1 ? ipsslAddressCount : null + dnsSuffix: dnsSuffix + frontEndScaleFactor: frontEndScaleFactor + clusterSettings: clusterSettings + userWhitelistedIpRanges: !empty(userWhitelistedIpRanges) ? userWhitelistedIpRanges : null + dedicatedHostCount: dedicatedHostCount != -1 ? dedicatedHostCount : null + zoneRedundant: zoneRedundant + } +} + +resource appServiceEnvironment_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${appServiceEnvironment.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: appServiceEnvironment +} + +resource appServiceEnvironment_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: appServiceEnvironment +} + +module appServiceEnvironment_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppServiceEnv-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: appServiceEnvironment.id + } +}] + +@description('The resource ID of the app service environment.') +output resourceId string = appServiceEnvironment.id + +@description('The resource group the app service environment was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the app service environment.') +output name string = appServiceEnvironment.name + +@description('The location the resource was deployed into.') +output location string = appServiceEnvironment.location diff --git a/modules/Microsoft.Web/hostingEnvironments/readme.md b/modules/Microsoft.Web/hostingEnvironments/readme.md new file mode 100644 index 0000000..f5fd364 --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/readme.md @@ -0,0 +1,420 @@ +# App Service Environments `[Microsoft.Web/hostingEnvironments]` + +This module deploys an app service environment. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Web/hostingEnvironments` | [2021-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/2021-03-01/hostingEnvironments) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the App Service Environment. | +| `subnetResourceId` | string | ResourceId for the subnet. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `clusterSettings` | array | `[System.Collections.Hashtable]` | | Custom settings for changing the behavior of the App Service Environment. | +| `dedicatedHostCount` | int | `-1` | | The Dedicated Host Count. Is not supported by ASEv2. If `zoneRedundant` is false, and you want physical hardware isolation enabled, set to 2. Otherwise 0. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[AppServiceEnvironmentPlatformLogs]` | `[AppServiceEnvironmentPlatformLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `dnsSuffix` | string | `''` | | DNS suffix of the App Service Environment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `frontEndScaleFactor` | int | `15` | | Scale factor for frontends. | +| `internalLoadBalancingMode` | string | `'None'` | `[None, Publishing, Web]` | Specifies which endpoints to serve internally in the Virtual Network for the App Service Environment. - None, Web, Publishing, Web,Publishing. | +| `ipsslAddressCount` | int | `-1` | | Number of IP SSL addresses reserved for the App Service Environment. | +| `kind` | string | `'ASEv3'` | | Kind of resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `multiSize` | string | `''` | `['', ExtraLarge, Large, Medium, Standard_D1_V2, Standard_D2, Standard_D2_V2, Standard_D3, Standard_D3_V2, Standard_D4, Standard_D4_V2]` | Frontend VM size. Cannot be used with 'kind' `ASEv3`. | +| `roleAssignments` | array | `[]` | | 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}` | | Resource tags. | +| `userWhitelistedIpRanges` | array | `[]` | | User added IP ranges to whitelist on ASE DB. Cannot be used with 'kind' `ASEv3`. | +| `zoneRedundant` | bool | `False` | | Switch to make the App Service Environment zone redundant. If enabled, the minimum App Service plan instance count will be three, otherwise 1. If enabled, the `dedicatedHostCount` must be set to `-1`. | + + +### Parameter Usage: `clusterSettings` + +

+ +Parameter JSON format + +```json +"clusterSettings": { + "value": [ + { + "name": "DisableTls1.0", + "value": "1" + } + ] +} +``` + +
+ + +
+ +Bicep format + +```bicep +clusterSettings: [ + { + name: 'DisableTls1.0' + value: '1' + } +] +``` + +
+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +
+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the app service environment. | +| `resourceGroupName` | string | The resource group the app service environment was deployed into. | +| `resourceId` | string | The resource ID of the app service environment. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Asev2

+ +
+ +via Bicep module + +```bicep +module hostingEnvironments './Microsoft.Web/hostingEnvironments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-HostingEnvironments' + params: { + // Required parameters + name: '<>-az-appse-asev2-001' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-008' + // Non-required parameters + clusterSettings: [ + { + name: 'DisableTls1.0' + value: '1' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + ipsslAddressCount: 2 + kind: 'ASEv2' + multiSize: 'Standard_D1_V2' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-appse-asev2-001" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-008" + }, + // Non-required parameters + "clusterSettings": { + "value": [ + { + "name": "DisableTls1.0", + "value": "1" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "ipsslAddressCount": { + "value": 2 + }, + "kind": { + "value": "ASEv2" + }, + "multiSize": { + "value": "Standard_D1_V2" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Asev3

+ +
+ +via Bicep module + +```bicep +module hostingEnvironments './Microsoft.Web/hostingEnvironments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-HostingEnvironments' + params: { + // Required parameters + name: '<>-az-appse-asev3-001' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-006' + // Non-required parameters + clusterSettings: [ + { + name: 'DisableTls1.0' + value: '1' + } + ] + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-appse-asev3-001" + }, + "subnetResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-006" + }, + // Non-required parameters + "clusterSettings": { + "value": [ + { + "name": "DisableTls1.0", + "value": "1" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Web/hostingEnvironments/version.json b/modules/Microsoft.Web/hostingEnvironments/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/hostingEnvironments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/serverfarms/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Web/serverfarms/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..6fd1e42 --- /dev/null +++ b/modules/Microsoft.Web/serverfarms/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + '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') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') +} + +resource appServicePlan 'Microsoft.Web/serverfarms@2021-02-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appServicePlan.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appServicePlan +}] diff --git a/modules/Microsoft.Web/serverfarms/.test/parameters.json b/modules/Microsoft.Web/serverfarms/.test/parameters.json new file mode 100644 index 0000000..63e6aa9 --- /dev/null +++ b/modules/Microsoft.Web/serverfarms/.test/parameters.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-asp-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": { + "name": "S1", + "tier": "Standard", + "size": "S1", + "family": "S", + "capacity": "1" + } + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + } + } +} diff --git a/modules/Microsoft.Web/serverfarms/deploy.bicep b/modules/Microsoft.Web/serverfarms/deploy.bicep new file mode 100644 index 0000000..a5a7210 --- /dev/null +++ b/modules/Microsoft.Web/serverfarms/deploy.bicep @@ -0,0 +1,189 @@ +// ================ // +// Parameters // +// ================ // +@description('Required. The name of the app service plan to deploy.') +@minLength(1) +@maxLength(40) +param name string + +@description('Required. Defines the name, tier, size, family and capacity of the App Service Plan.') +param sku object + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Kind of server OS.') +@allowed([ + 'Windows' + 'Linux' +]) +param serverOS string = 'Windows' + +@description('Optional. The Resource ID of the App Service Environment to use for the App Service Plan.') +param appServiceEnvironmentId string = '' + +@description('Optional. Target worker tier assigned to the App Service plan.') +param workerTierName string = '' + +@description('Optional. If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan.') +param perSiteScaling bool = false + +@description('Optional. Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan.') +param maximumElasticWorkerCount int = 1 + +@description('Optional. Scaling worker count.') +param targetWorkerCount int = 0 + +@description('Optional. The instance size of the hosting plan (small, medium, or large).') +@allowed([ + 0 + 1 + 2 +]) +param targetWorkerSize int = 0 + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@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. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. When true, this App Service Plan will perform availability zone balancing.') +param zoneRedundant bool = false + +// =========== // +// Variables // +// =========== // +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appServicePlan 'Microsoft.Web/serverfarms@2021-02-01' = { + name: name + kind: serverOS == 'Windows' ? '' : 'linux' + location: location + tags: tags + sku: sku + properties: { + workerTierName: workerTierName + hostingEnvironmentProfile: !empty(appServiceEnvironmentId) ? { + id: appServiceEnvironmentId + } : null + perSiteScaling: perSiteScaling + maximumElasticWorkerCount: maximumElasticWorkerCount + reserved: serverOS == 'Linux' + targetWorkerCount: targetWorkerCount + targetWorkerSizeId: targetWorkerSize + zoneRedundant: zoneRedundant + } +} + +resource appServicePlan_diagnosticSettings 'Microsoft.Insights/diagnosticsettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: [] + } + scope: appServicePlan +} + +resource appServicePlan_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${appServicePlan.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: appServicePlan +} + +module appServicePlan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppServicePlan-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: appServicePlan.id + } +}] + +// =========== // +// Outputs // +// =========== // +@description('The resource group the app service plan was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the app service plan.') +output name string = appServicePlan.name + +@description('The resource ID of the app service plan.') +output resourceId string = appServicePlan.id + +@description('The location the resource was deployed into.') +output location string = appServicePlan.location diff --git a/modules/Microsoft.Web/serverfarms/readme.md b/modules/Microsoft.Web/serverfarms/readme.md new file mode 100644 index 0000000..ffad984 --- /dev/null +++ b/modules/Microsoft.Web/serverfarms/readme.md @@ -0,0 +1,309 @@ +# App Service Plans `[Microsoft.Web/serverfarms]` + +This module deploys an app service plan. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Web/serverfarms` | [2021-02-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/2021-02-01/serverfarms) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the app service plan to deploy. | +| `sku` | object | Defines the name, tier, size, family and capacity of the App Service Plan. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `appServiceEnvironmentId` | string | `''` | | The Resource ID of the App Service Environment to use for the App Service Plan. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maximumElasticWorkerCount` | int | `1` | | Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan. | +| `perSiteScaling` | bool | `False` | | If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan. | +| `roleAssignments` | array | `[]` | | 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'. | +| `serverOS` | string | `'Windows'` | `[Linux, Windows]` | Kind of server OS. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `targetWorkerCount` | int | `0` | | Scaling worker count. | +| `targetWorkerSize` | int | `0` | `[0, 1, 2]` | The instance size of the hosting plan (small, medium, or large). | +| `workerTierName` | string | `''` | | Target worker tier assigned to the App Service plan. | +| `zoneRedundant` | bool | `False` | | When true, this App Service Plan will perform availability zone balancing. | + + +### Parameter Usage: `sku` + +

+ +Parameter JSON format + +```json +"sku": { + "value": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + } +} +``` + +
+ +
+ +Bicep format + +```bicep +sku: { + name: 'P1v2' + tier: 'PremiumV2' + size: 'P1v2' + family: 'Pv2' + capacity: 1 +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the app service plan. | +| `resourceGroupName` | string | The resource group the app service plan was deployed into. | +| `resourceId` | string | The resource ID of the app service plan. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Parameters

+ +
+ +via Bicep module + +```bicep +module serverfarms './Microsoft.Web/serverfarms/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Serverfarms' + params: { + // Required parameters + name: '<>-az-asp-x-001' + sku: { + capacity: '1' + family: 'S' + name: 'S1' + size: 'S1' + tier: 'Standard' + } + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-az-asp-x-001" + }, + "sku": { + "value": { + "capacity": "1", + "family": "S", + "name": "S1", + "size": "S1", + "tier": "Standard" + } + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Web/serverfarms/version.json b/modules/Microsoft.Web/serverfarms/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/serverfarms/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/sites/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Web/sites/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..260bbb2 --- /dev/null +++ b/modules/Microsoft.Web/sites/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId 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') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + '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') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') +} + +resource app 'Microsoft.Web/sites@2020-12-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(app.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: app +}] diff --git a/modules/Microsoft.Web/sites/.test/fa.min.parameters.json b/modules/Microsoft.Web/sites/.test/fa.min.parameters.json new file mode 100644 index 0000000..0d4b5e8 --- /dev/null +++ b/modules/Microsoft.Web/sites/.test/fa.min.parameters.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fa-min-001" + }, + "kind": { + "value": "functionapp" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + "siteConfig": { + "value": { + "alwaysOn": true + } + } + } +} diff --git a/modules/Microsoft.Web/sites/.test/fa.parameters.json b/modules/Microsoft.Web/sites/.test/fa.parameters.json new file mode 100644 index 0000000..a4b733f --- /dev/null +++ b/modules/Microsoft.Web/sites/.test/fa.parameters.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-fa-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "kind": { + "value": "functionapp" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + "siteConfig": { + "value": { + "alwaysOn": true, + "use32BitWorkerProcess": false + } + }, + "appInsightId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "setAzureWebJobsDashboard": { + "value": true + }, + "appSettingsKeyValuePairs": { + "value": { + "FUNCTIONS_EXTENSION_VERSION": "~4", + "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "AzureFunctionsJobHost__logging__logLevel__default": "Trace", + "EASYAUTH_SECRET": "https://adp-<>-az-kv-x-001.vault.azure.net/secrets/Modules-Test-SP-Password" + } + }, + "authSettingV2Configuration": { + "value": { + "globalValidation": { + "requireAuthentication": true, + "unauthenticatedClientAction": "Return401" + }, + "httpSettings": { + "forwardProxy": { + "convention": "NoProxy" + }, + "requireHttps": true, + "routes": { + "apiPrefix": "/.auth" + } + }, + "identityProviders": { + "azureActiveDirectory": { + "enabled": true, + "login": { + "disableWWWAuthenticate": false + }, + "registration": { + "openIdIssuer": "https://sts.windows.net/<>/v2.0/", + "clientId": "d874dd2f-2032-4db1-a053-f0ec243685aa", + "clientSecretSettingName": "EASYAUTH_SECRET" + }, + "validation": { + "allowedAudiences": [ + "api://d874dd2f-2032-4db1-a053-f0ec243685aa" + ], + "defaultAuthorizationPolicy": { + "allowedPrincipals": {} + }, + "jwtClaimChecks": {} + } + } + }, + "login": { + "allowedExternalRedirectUrls": [ + "string" + ], + "cookieExpiration": { + "convention": "FixedTime", + "timeToExpiration": "08:00:00" + }, + "nonce": { + "nonceExpirationInterval": "00:05:00", + "validateNonce": true + }, + "preserveUrlFragmentsForLogins": false, + "routes": {}, + "tokenStore": { + "azureBlobStorage": {}, + "enabled": true, + "fileSystem": {}, + "tokenRefreshExtensionHours": 72 + } + }, + "platform": { + "enabled": true, + "runtimeVersion": "~1" + } + } + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "sites", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Web/sites/.test/wa.min.parameters.json b/modules/Microsoft.Web/sites/.test/wa.min.parameters.json new file mode 100644 index 0000000..588beef --- /dev/null +++ b/modules/Microsoft.Web/sites/.test/wa.min.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-wa-min-001" + }, + "kind": { + "value": "app" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + } + } +} diff --git a/modules/Microsoft.Web/sites/.test/wa.parameters.json b/modules/Microsoft.Web/sites/.test/wa.parameters.json new file mode 100644 index 0000000..ad18626 --- /dev/null +++ b/modules/Microsoft.Web/sites/.test/wa.parameters.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-wa-x-001" + }, + "kind": { + "value": "app" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + "siteConfig": { + "value": { + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnetcore" + } + ], + "alwaysOn": true + } + }, + "httpsOnly": { + "value": true + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "sites", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Web/sites/config-appsettings/deploy.bicep b/modules/Microsoft.Web/sites/config-appsettings/deploy.bicep new file mode 100644 index 0000000..d8ab337 --- /dev/null +++ b/modules/Microsoft.Web/sites/config-appsettings/deploy.bicep @@ -0,0 +1,95 @@ +// ================ // +// Parameters // +// ================ // +@description('Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment.') +param appName string + +@description('Required. Type of site to deploy.') +@allowed([ + 'functionapp' + 'functionapp,linux' + 'app' +]) +param kind string + +@description('Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions.') +param storageAccountId string = '' + +@description('Optional. Resource ID of the app insight to leverage for this resource.') +param appInsightId string = '' + +@description('Optional. For function apps. If true the app settings "AzureWebJobsDashboard" will be set. If false not. In case you use Application Insights it can make sense to not set it for performance reasons.') +param setAzureWebJobsDashboard bool = contains(kind, 'functionapp') ? true : false + +@description('Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING.') +param appSettingsKeyValuePairs object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Variables // +// =========== // +var azureWebJobsValues = !empty(storageAccountId) ? union({ + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};' + }, ((setAzureWebJobsDashboard == true) ? { + AzureWebJobsDashboard: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};' + } : {})) : {} + +var appInsightsValues = !empty(appInsightId) ? { + APPINSIGHTS_INSTRUMENTATIONKEY: appInsight.properties.InstrumentationKey + APPLICATIONINSIGHTS_CONNECTION_STRING: appInsight.properties.ConnectionString +} : {} + +var expandedAppSettings = union(appSettingsKeyValuePairs, azureWebJobsValues, appInsightsValues) + +// =========== // +// Existing resources // +// =========== // +resource app 'Microsoft.Web/sites@2020-12-01' existing = { + name: appName +} + +resource appInsight 'microsoft.insights/components@2020-02-02' existing = if (!empty(appInsightId)) { + name: last(split(appInsightId, '/')) + scope: resourceGroup(split(appInsightId, '/')[2], split(appInsightId, '/')[4]) +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' existing = if (!empty(storageAccountId)) { + name: last(split(storageAccountId, '/')) + scope: resourceGroup(split(storageAccountId, '/')[2], split(storageAccountId, '/')[4]) +} + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appSettings 'Microsoft.Web/sites/config@2020-12-01' = { + name: 'appsettings' + kind: kind + parent: app + properties: expandedAppSettings +} + +// =========== // +// Outputs // +// =========== // +@description('The name of the site config.') +output name string = appSettings.name + +@description('The resource ID of the site config.') +output resourceId string = appSettings.id + +@description('The resource group the site config was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Web/sites/config-appsettings/readme.md b/modules/Microsoft.Web/sites/config-appsettings/readme.md new file mode 100644 index 0000000..5d5fdf1 --- /dev/null +++ b/modules/Microsoft.Web/sites/config-appsettings/readme.md @@ -0,0 +1,96 @@ +# Site Config `[Microsoft.Web/sites/config-appsettings]` + +This module deploys the app settings. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Web/sites/config` | [2020-12-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `kind` | string | `[app, functionapp, functionapp,linux]` | Type of site to deploy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appName` | string | The name of the parent site resource. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `appInsightId` | string | `''` | Resource ID of the app insight to leverage for this resource. | +| `appSettingsKeyValuePairs` | object | `{object}` | The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `setAzureWebJobsDashboard` | bool | `[if(contains(parameters('kind'), 'functionapp'), true(), false())]` | For function apps. If true the app settings "AzureWebJobsDashboard" will be set. If false not. In case you use Application Insights it can make sense to not set it for performance reasons. | +| `storageAccountId` | string | `''` | Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. | + + +### Parameter Usage: `appSettingsKeyValuePairs` + +AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING are set separately (check parameters storageAccountId, setAzureWebJobsDashboard, appInsightId). +For all other app settings key-value pairs use this object. + +

+ +Parameter JSON format + +```json +"appSettingsKeyValuePairs": { + "value": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +appSettingsKeyValuePairs: [ + { + name: 'key1' + value: 'val1' + } + { + name: 'key2' + value: 'val2' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the site config. | +| `resourceGroupName` | string | The resource group the site config was deployed into. | +| `resourceId` | string | The resource ID of the site config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Web/sites/config-appsettings/version.json b/modules/Microsoft.Web/sites/config-appsettings/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/sites/config-appsettings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/sites/config-authsettingsv2/deploy.bicep b/modules/Microsoft.Web/sites/config-authsettingsv2/deploy.bicep new file mode 100644 index 0000000..a8ca1f4 --- /dev/null +++ b/modules/Microsoft.Web/sites/config-authsettingsv2/deploy.bicep @@ -0,0 +1,60 @@ +// ================ // +// Parameters // +// ================ // +@description('Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment.') +param appName string + +@description('Required. Type of site to deploy.') +@allowed([ + 'functionapp' + 'functionapp,linux' + 'app' +]) +param kind string + +@description('Required. The auth settings V2 configuration.') +param authSettingV2Configuration object + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Existing resources // +// =========== // +resource app 'Microsoft.Web/sites@2020-12-01' existing = { + name: appName +} + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appSettings 'Microsoft.Web/sites/config@2020-12-01' = { + name: 'authsettingsV2' + kind: kind + parent: app + properties: authSettingV2Configuration +} + +// =========== // +// Outputs // +// =========== // +@description('The name of the site config.') +output name string = appSettings.name + +@description('The resource ID of the site config.') +output resourceId string = appSettings.id + +@description('The resource group the site config was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Web/sites/config-authsettingsv2/readme.md b/modules/Microsoft.Web/sites/config-authsettingsv2/readme.md new file mode 100644 index 0000000..2cee567 --- /dev/null +++ b/modules/Microsoft.Web/sites/config-authsettingsv2/readme.md @@ -0,0 +1,78 @@ +# Site Config `[Microsoft.Web/sites/config-authsettingsv2]` + +This module deploys the auth settings v2. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Web/sites/config` | [2020-12-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `authSettingV2Configuration` | object | | The auth settings V2 configuration. | +| `kind` | string | `[app, functionapp, functionapp,linux]` | Type of site to deploy. | + +**Conditional parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appName` | string | The name of the parent site resource. Required if the template is used in a standalone deployment. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | + + +### Parameter Usage: `authSettingV2Configuration` + +The auth settings V2 configuration. + +

+ +Parameter JSON format + +```json +"siteConfig": { + "value": [ + // Check out https://docs.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?tabs=bicep#siteauthsettingsv2properties for possible properties + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +siteConfig: [ + // Check out https://docs.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?tabs=bicep#siteauthsettingsv2properties for possible properties +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the site config. | +| `resourceGroupName` | string | The resource group the site config was deployed into. | +| `resourceId` | string | The resource ID of the site config. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Web/sites/config-authsettingsv2/version.json b/modules/Microsoft.Web/sites/config-authsettingsv2/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/sites/config-authsettingsv2/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/sites/deploy.bicep b/modules/Microsoft.Web/sites/deploy.bicep new file mode 100644 index 0000000..eaa1b7b --- /dev/null +++ b/modules/Microsoft.Web/sites/deploy.bicep @@ -0,0 +1,299 @@ +// ================ // +// Parameters // +// ================ // +// General +@description('Required. Name of the site.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Required. Type of site to deploy.') +@allowed([ + 'functionapp' + 'functionapp,linux' + 'app' +]) +param kind string + +@description('Required. The resource ID of the app service plan to use for the site.') +param serverFarmResourceId string + +@description('Optional. Configures a site to accept only HTTPS requests. Issues redirect for HTTP requests.') +param httpsOnly bool = true + +@description('Optional. If client affinity is enabled.') +param clientAffinityEnabled bool = true + +@description('Optional. The resource ID of the app service environment to use for this resource.') +param appServiceEnvironmentId string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Checks if Customer provided storage account is required.') +param storageAccountRequired bool = false + +@description('Optional. Azure Resource Manager ID of the Virtual network and subnet to be joined by Regional VNET Integration. This must be of the form /subscriptions/{subscriptionName}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}.') +param virtualNetworkSubnetId string = '' + +// Site Config +@description('Optional. The site config object.') +param siteConfig object = {} + +@description('Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions.') +param storageAccountId string = '' + +@description('Optional. Resource ID of the app insight to leverage for this resource.') +param appInsightId string = '' + +@description('Optional. For function apps. If true the app settings "AzureWebJobsDashboard" will be set. If false not. In case you use Application Insights it can make sense to not set it for performance reasons.') +param setAzureWebJobsDashboard bool = contains(kind, 'functionapp') ? true : false + +@description('Optional. The app settings-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING.') +param appSettingsKeyValuePairs object = {} + +@description('Optional. The auth settings V2 configuration.') +param authSettingV2Configuration object = {} + +// Lock +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +// Private Endpoints +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +// Tags +@description('Optional. Tags of the resource.') +param tags object = {} + +// PID +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +// Role Assignments +@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 = [] + +// Diagnostic Settings +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of log analytics workspace.') +param diagnosticWorkspaceId string = '' + +@description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') +param diagnosticEventHubAuthorizationRuleId string = '' + +@description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed.') +@allowed([ + 'AppServiceHTTPLogs' + 'AppServiceConsoleLogs' + 'AppServiceAppLogs' + 'AppServiceAuditLogs' + 'AppServiceIPSecAuditLogs' + 'AppServicePlatformLogs' + 'FunctionAppLogs' +]) +param diagnosticLogCategoriesToEnable array = kind == 'functionapp' ? [ + 'FunctionAppLogs' +] : [ + 'AppServiceHTTPLogs' + 'AppServiceConsoleLogs' + 'AppServiceAppLogs' + 'AppServiceAuditLogs' + 'AppServiceIPSecAuditLogs' + 'AppServicePlatformLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed.') +param diagnosticSettingsName string = '${name}-diagnosticSettings' + +// =========== // +// Variables // +// =========== // +var diagnosticsLogs = [for category in diagnosticLogCategoriesToEnable: { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +// =========== // +// Deployments // +// =========== // +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource app 'Microsoft.Web/sites@2021-03-01' = { + name: name + location: location + kind: kind + tags: tags + identity: identity + properties: { + serverFarmId: serverFarmResourceId + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: httpsOnly + hostingEnvironmentProfile: !empty(appServiceEnvironmentId) ? { + id: appServiceEnvironmentId + } : null + storageAccountRequired: storageAccountRequired + virtualNetworkSubnetId: !empty(virtualNetworkSubnetId) ? virtualNetworkSubnetId : any(null) + siteConfig: siteConfig + } +} + +module app_appsettings 'config-appsettings/deploy.bicep' = if (!empty(appSettingsKeyValuePairs)) { + name: '${uniqueString(deployment().name, location)}-Site-Config-AppSettings' + params: { + appName: app.name + kind: kind + storageAccountId: storageAccountId + appInsightId: appInsightId + setAzureWebJobsDashboard: setAzureWebJobsDashboard + appSettingsKeyValuePairs: appSettingsKeyValuePairs + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module app_authsettingsv2 'config-authsettingsv2/deploy.bicep' = if (!empty(authSettingV2Configuration)) { + name: '${uniqueString(deployment().name, location)}-Site-Config-AuthSettingsV2' + params: { + appName: app.name + kind: kind + authSettingV2Configuration: authSettingV2Configuration + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource app_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${app.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: app +} + +resource app_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: diagnosticSettingsName + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: app +} + +module app_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Site-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: app.id + } +}] + +module app_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-Site-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(app.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: app.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +// =========== // +// Outputs // +// =========== // +@description('The name of the site.') +output name string = app.name + +@description('The resource ID of the site.') +output resourceId string = app.id + +@description('The resource group the site was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(app.identity, 'principalId') ? app.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = app.location + +@description('Default hostname of the app.') +output defaultHostname string = app.properties.defaultHostName diff --git a/modules/Microsoft.Web/sites/readme.md b/modules/Microsoft.Web/sites/readme.md new file mode 100644 index 0000000..6fe2a34 --- /dev/null +++ b/modules/Microsoft.Web/sites/readme.md @@ -0,0 +1,952 @@ +# Web/Function Apps `[Microsoft.Web/sites]` + +This module deploys a web or function app. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Web/sites` | [2021-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/2021-03-01/sites) | +| `Microsoft.Web/sites/config` | [2020-12-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `kind` | string | `[app, functionapp, functionapp,linux]` | Type of site to deploy. | +| `name` | string | | Name of the site. | +| `serverFarmResourceId` | string | | The resource ID of the app service plan to use for the site. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `appInsightId` | string | `''` | | Resource ID of the app insight to leverage for this resource. | +| `appServiceEnvironmentId` | string | `''` | | The resource ID of the app service environment to use for this resource. | +| `appSettingsKeyValuePairs` | object | `{object}` | | The app settings-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING. | +| `authSettingV2Configuration` | object | `{object}` | | The auth settings V2 configuration. | +| `clientAffinityEnabled` | bool | `True` | | If client affinity is enabled. | +| `diagnosticEventHubAuthorizationRuleId` | string | `''` | | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| `diagnosticEventHubName` | string | `''` | | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. | +| `diagnosticLogCategoriesToEnable` | array | `[if(equals(parameters('kind'), 'functionapp'), createArray('FunctionAppLogs'), createArray('AppServiceHTTPLogs', 'AppServiceConsoleLogs', 'AppServiceAppLogs', 'AppServiceAuditLogs', 'AppServiceIPSecAuditLogs', 'AppServicePlatformLogs'))]` | `[AppServiceAppLogs, AppServiceAuditLogs, AppServiceConsoleLogs, AppServiceHTTPLogs, AppServiceIPSecAuditLogs, AppServicePlatformLogs, FunctionAppLogs]` | The name of logs that will be streamed. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `[format('{0}-diagnosticSettings', parameters('name'))]` | | The name of the diagnostic setting, if deployed. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `httpsOnly` | bool | `True` | | Configures a site to accept only HTTPS requests. Issues redirect for HTTP requests. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `roleAssignments` | array | `[]` | | 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'. | +| `setAzureWebJobsDashboard` | bool | `[if(contains(parameters('kind'), 'functionapp'), true(), false())]` | | For function apps. If true the app settings "AzureWebJobsDashboard" will be set. If false not. In case you use Application Insights it can make sense to not set it for performance reasons. | +| `siteConfig` | object | `{object}` | | The site config object. | +| `storageAccountId` | string | `''` | | Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. | +| `storageAccountRequired` | bool | `False` | | Checks if Customer provided storage account is required. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `virtualNetworkSubnetId` | string | `''` | | Azure Resource Manager ID of the Virtual network and subnet to be joined by Regional VNET Integration. This must be of the form /subscriptions/{subscriptionName}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}. | + + +### Parameter Usage: `appSettingsKeyValuePairs` + +AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING are set separately (check parameters storageAccountId, setAzureWebJobsDashboard, appInsightId). +For all other app settings key-value pairs use this object. + +

+ +Parameter JSON format + +```json +"appSettingsKeyValuePairs": { + "value": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +appSettingsKeyValuePairs: [ + { + name: 'key1' + value: 'val1' + } + { + name: 'key2' + value: 'val2' + } +] +``` + +
+

+ +### Parameter Usage: `authSettingV2Configuration` + +The auth settings V2 configuration. + +

+ +Parameter JSON format + +```json +"siteConfig": { + "value": [ + // Check out https://docs.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?tabs=bicep#siteauthsettingsv2properties for possible properties + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +siteConfig: [ + // Check out https://docs.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?tabs=bicep#siteauthsettingsv2properties for possible properties +] +``` + +
+

+ +### Parameter Usage: `siteConfig` + +The site config. + +

+ +Parameter JSON format + +```json +"siteConfig": { + "value": [ + // Check out https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/sites?tabs=bicep#siteconfig for possible properties + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +siteConfig: [ + // Check out https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/sites?tabs=bicep#siteconfig for possible properties +] +``` + +
+

+ +### 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `defaultHostname` | string | Default hostname of the app. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the site. | +| `resourceGroupName` | string | The resource group the site was deployed into. | +| `resourceId` | string | The resource ID of the site. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Fa Min

+ +
+ +via Bicep module + +```bicep +module sites './Microsoft.Web/sites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Sites' + params: { + // Required parameters + kind: 'functionapp' + name: '<>-az-fa-min-001' + serverFarmResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001' + // Non-required parameters + siteConfig: { + alwaysOn: true + } + } +} +``` + +
+

+ +

+ +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": "functionapp" + }, + "name": { + "value": "<>-az-fa-min-001" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + // Non-required parameters + "siteConfig": { + "value": { + "alwaysOn": true + } + } + } +} +``` + +
+

+ +

Example 2: Fa

+ +
+ +via Bicep module + +```bicep +module sites './Microsoft.Web/sites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Sites' + params: { + // Required parameters + kind: 'functionapp' + name: '<>-az-fa-x-001' + serverFarmResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001' + // Non-required parameters + appInsightId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001' + appSettingsKeyValuePairs: { + AzureFunctionsJobHost__logging__logLevel__default: 'Trace' + EASYAUTH_SECRET: 'https://adp-<>-az-kv-x-001.vault.azure.net/secrets/Modules-Test-SP-Password' + FUNCTIONS_EXTENSION_VERSION: '~4' + FUNCTIONS_WORKER_RUNTIME: 'dotnet' + } + authSettingV2Configuration: { + globalValidation: { + requireAuthentication: true + unauthenticatedClientAction: 'Return401' + } + httpSettings: { + forwardProxy: { + convention: 'NoProxy' + } + requireHttps: true + routes: { + apiPrefix: '/.auth' + } + } + identityProviders: { + azureActiveDirectory: { + enabled: true + login: { + disableWWWAuthenticate: false + } + registration: { + clientId: 'd874dd2f-2032-4db1-a053-f0ec243685aa' + clientSecretSettingName: 'EASYAUTH_SECRET' + openIdIssuer: 'https://sts.windows.net/<>/v2.0/' + } + validation: { + allowedAudiences: [ + 'api://d874dd2f-2032-4db1-a053-f0ec243685aa' + ] + defaultAuthorizationPolicy: { + allowedPrincipals: {} + } + jwtClaimChecks: {} + } + } + } + login: { + allowedExternalRedirectUrls: [ + 'string' + ] + cookieExpiration: { + convention: 'FixedTime' + timeToExpiration: '08:00:00' + } + nonce: { + nonceExpirationInterval: '00:05:00' + validateNonce: true + } + preserveUrlFragmentsForLogins: false + routes: {} + tokenStore: { + azureBlobStorage: {} + enabled: true + fileSystem: {} + tokenRefreshExtensionHours: 72 + } + } + platform: { + enabled: true + runtimeVersion: '~1' + } + } + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net' + ] + } + service: 'sites' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + setAzureWebJobsDashboard: true + siteConfig: { + alwaysOn: true + use32BitWorkerProcess: false + } + storageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "functionapp" + }, + "name": { + "value": "<>-az-fa-x-001" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + // Non-required parameters + "appInsightId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Insights/components/adp-<>-az-appi-x-001" + }, + "appSettingsKeyValuePairs": { + "value": { + "AzureFunctionsJobHost__logging__logLevel__default": "Trace", + "EASYAUTH_SECRET": "https://adp-<>-az-kv-x-001.vault.azure.net/secrets/Modules-Test-SP-Password", + "FUNCTIONS_EXTENSION_VERSION": "~4", + "FUNCTIONS_WORKER_RUNTIME": "dotnet" + } + }, + "authSettingV2Configuration": { + "value": { + "globalValidation": { + "requireAuthentication": true, + "unauthenticatedClientAction": "Return401" + }, + "httpSettings": { + "forwardProxy": { + "convention": "NoProxy" + }, + "requireHttps": true, + "routes": { + "apiPrefix": "/.auth" + } + }, + "identityProviders": { + "azureActiveDirectory": { + "enabled": true, + "login": { + "disableWWWAuthenticate": false + }, + "registration": { + "clientId": "d874dd2f-2032-4db1-a053-f0ec243685aa", + "clientSecretSettingName": "EASYAUTH_SECRET", + "openIdIssuer": "https://sts.windows.net/<>/v2.0/" + }, + "validation": { + "allowedAudiences": [ + "api://d874dd2f-2032-4db1-a053-f0ec243685aa" + ], + "defaultAuthorizationPolicy": { + "allowedPrincipals": {} + }, + "jwtClaimChecks": {} + } + } + }, + "login": { + "allowedExternalRedirectUrls": [ + "string" + ], + "cookieExpiration": { + "convention": "FixedTime", + "timeToExpiration": "08:00:00" + }, + "nonce": { + "nonceExpirationInterval": "00:05:00", + "validateNonce": true + }, + "preserveUrlFragmentsForLogins": false, + "routes": {}, + "tokenStore": { + "azureBlobStorage": {}, + "enabled": true, + "fileSystem": {}, + "tokenRefreshExtensionHours": 72 + } + }, + "platform": { + "enabled": true, + "runtimeVersion": "~1" + } + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net" + ] + }, + "service": "sites", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "setAzureWebJobsDashboard": { + "value": true + }, + "siteConfig": { + "value": { + "alwaysOn": true, + "use32BitWorkerProcess": false + } + }, + "storageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

+ +

Example 3: Wa Min

+ +
+ +via Bicep module + +```bicep +module sites './Microsoft.Web/sites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Sites' + params: { + // Required parameters + kind: 'app' + name: '<>-az-wa-min-001' + serverFarmResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001' + } +} +``` + +
+

+ +

+ +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": "app" + }, + "name": { + "value": "<>-az-wa-min-001" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + } + } +} +``` + +
+

+ +

Example 4: Wa

+ +
+ +via Bicep module + +```bicep +module sites './Microsoft.Web/sites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-Sites' + params: { + // Required parameters + kind: 'app' + name: '<>-az-wa-x-001' + serverFarmResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey' + diagnosticEventHubName: 'adp-<>-az-evh-x-001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001' + diagnosticWorkspaceId: '/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001' + httpsOnly: true + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net' + ] + } + service: 'sites' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + siteConfig: { + alwaysOn: true + metadata: [ + { + name: 'CURRENT_STACK' + value: 'dotnetcore' + } + ] + } + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "app" + }, + "name": { + "value": "<>-az-wa-x-001" + }, + "serverFarmResourceId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Web/serverFarms/adp-<>-az-asp-x-001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.EventHub/namespaces/adp-<>-az-evhns-x-001/AuthorizationRules/RootManageSharedAccessKey" + }, + "diagnosticEventHubName": { + "value": "adp-<>-az-evh-x-001" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Storage/storageAccounts/adp<>azsax001" + }, + "diagnosticWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-<>-az-law-x-001" + }, + "httpsOnly": { + "value": true + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net" + ] + }, + "service": "sites", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "siteConfig": { + "value": { + "alwaysOn": true, + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnetcore" + } + ] + } + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Web/sites/version.json b/modules/Microsoft.Web/sites/version.json new file mode 100644 index 0000000..56f8d9c --- /dev/null +++ b/modules/Microsoft.Web/sites/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Web/staticSites/.bicep/nested_roleAssignments.bicep b/modules/Microsoft.Web/staticSites/.bicep/nested_roleAssignments.bicep new file mode 100644 index 0000000..5445b96 --- /dev/null +++ b/modules/Microsoft.Web/staticSites/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,34 @@ +param principalIds array +param principalType string = '' +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 staticSite 'Microsoft.Web/staticSites@2021-02-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(staticSite.id, principalId, roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + } + scope: staticSite +}] diff --git a/modules/Microsoft.Web/staticSites/.test/min.parameters.json b/modules/Microsoft.Web/staticSites/.test/min.parameters.json new file mode 100644 index 0000000..b5781f4 --- /dev/null +++ b/modules/Microsoft.Web/staticSites/.test/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": "<>-az-wss-min-001" + } + } +} diff --git a/modules/Microsoft.Web/staticSites/.test/parameters.json b/modules/Microsoft.Web/staticSites/.test/parameters.json new file mode 100644 index 0000000..c15a52c --- /dev/null +++ b/modules/Microsoft.Web/staticSites/.test/parameters.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-wss-x-001" + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "Standard" + }, + "stagingEnvironmentPolicy": { + "value": "Enabled" + }, + "allowConfigFileUpdates": { + "value": true + }, + "enterpriseGradeCdnStatus": { + "value": "Disabled" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "staticSites", + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurestaticapps.net" + ] + } + } + ] + } + } +} diff --git a/modules/Microsoft.Web/staticSites/deploy.bicep b/modules/Microsoft.Web/staticSites/deploy.bicep new file mode 100644 index 0000000..3ebee32 --- /dev/null +++ b/modules/Microsoft.Web/staticSites/deploy.bicep @@ -0,0 +1,177 @@ +@description('Required. Name of the static site.') +@minLength(1) +@maxLength(40) +param name string + +@allowed([ + 'Free' + 'Standard' +]) +@description('Optional. Type of static site to deploy.') +param sku string = 'Free' + +@description('Optional. If config file is locked for this static web app.') +param allowConfigFileUpdates bool = true + +@description('Optional. Location to deploy static site. The following locations are supported: CentralUS, EastUS2, EastAsia, WestEurope, WestUS2.') +param location string = resourceGroup().location + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Optional. State indicating whether staging environments are allowed or not allowed for a static web app.') +param stagingEnvironmentPolicy string = 'Enabled' + +@allowed([ + 'Disabled' + 'Disabling' + 'Enabled' + 'Enabling' +]) +@description('Optional. State indicating the status of the enterprise grade CDN serving traffic to the static web app.') +param enterpriseGradeCdnStatus string = 'Disabled' + +@description('Optional. Build properties for the static site.') +param buildProperties object = {} + +@description('Optional. Template Options for the static site.') +param templateProperties object = {} + +@description('Optional. The provider that submitted the last deployment to the primary environment of the static site.') +param provider string = 'None' + +@secure() +@description('Optional. The Personal Access Token for accessing the GitHub repository.') +param repositoryToken string = '' + +@description('Optional. The name of the GitHub repository.') +param repositoryUrl string = '' + +@description('Optional. The branch name of the GitHub repository.') +param branch string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the \'sku\' to be \'Standard\'.') +param privateEndpoints array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@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 = [] + +var enableReferencedModulesTelemetry = false + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource staticSite 'Microsoft.Web/staticSites@2021-03-01' = { + name: name + location: location + tags: tags + identity: identity + sku: { + name: sku + tier: sku + } + properties: { + allowConfigFileUpdates: allowConfigFileUpdates + stagingEnvironmentPolicy: stagingEnvironmentPolicy + enterpriseGradeCdnStatus: enterpriseGradeCdnStatus + provider: !empty(provider) ? provider : 'None' + branch: !empty(branch) ? branch : null + buildProperties: !empty(buildProperties) ? buildProperties : null + repositoryToken: !empty(repositoryToken) ? repositoryToken : null + repositoryUrl: !empty(repositoryUrl) ? repositoryUrl : null + templateProperties: !empty(templateProperties) ? templateProperties : null + } +} + +resource staticSite_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${staticSite.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: staticSite +} + +module staticSite_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-StaticSite-Rbac-${index}' + params: { + principalIds: roleAssignment.principalIds + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: staticSite.id + } +}] + +module staticSite_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-StaticSite-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(staticSite.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: staticSite.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +@description('The name of the static site.') +output name string = staticSite.name + +@description('The resource ID of the static site.') +output resourceId string = staticSite.id + +@description('The resource group the static site was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(staticSite.identity, 'principalId') ? staticSite.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = staticSite.location + +@description('The default autogenerated hostname for the static site.') +output defaultHostname string = staticSite.properties.defaultHostname diff --git a/modules/Microsoft.Web/staticSites/readme.md b/modules/Microsoft.Web/staticSites/readme.md new file mode 100644 index 0000000..be44bef --- /dev/null +++ b/modules/Microsoft.Web/staticSites/readme.md @@ -0,0 +1,443 @@ +# Static Web Apps `[Microsoft.Web/staticSites]` + +This module deploys a Static Web App. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-08-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-08-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Web/staticSites` | [2021-03-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Web/2021-03-01/staticSites) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the static site. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowConfigFileUpdates` | bool | `True` | | If config file is locked for this static web app. | +| `branch` | string | `''` | | The branch name of the GitHub repository. | +| `buildProperties` | object | `{object}` | | Build properties for the static site. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `enterpriseGradeCdnStatus` | string | `'Disabled'` | `[Disabled, Disabling, Enabled, Enabling]` | State indicating the status of the enterprise grade CDN serving traffic to the static web app. | +| `location` | string | `[resourceGroup().location]` | | Location to deploy static site. The following locations are supported: CentralUS, EastUS2, EastAsia, WestEurope, WestUS2. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'sku' to be 'Standard'. | +| `provider` | string | `'None'` | | The provider that submitted the last deployment to the primary environment of the static site. | +| `repositoryToken` | secureString | `''` | | The Personal Access Token for accessing the GitHub repository. | +| `repositoryUrl` | string | `''` | | The name of the GitHub repository. | +| `roleAssignments` | array | `[]` | | 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'. | +| `sku` | string | `'Free'` | `[Free, Standard]` | Type of static site to deploy. | +| `stagingEnvironmentPolicy` | string | `'Enabled'` | `[Disabled, Enabled]` | State indicating whether staging environments are allowed or not allowed for a static web app. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `templateProperties` | object | `{object}` | | Template Options for the static site. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to 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. + +

+ +Parameter JSON format + +```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": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // 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/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, 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": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // 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: '' // e.g. vault, registry, blob + privateDnsZoneGroups: { + privateDNSResourceIds: [ // 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/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + // Optional + customDnsConfigs: [ + { + 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: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "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 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + 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 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `defaultHostname` | string | The default autogenerated hostname for the static site. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the static site. | +| `resourceGroupName` | string | The resource group the static site was deployed into. | +| `resourceId` | string | The resource ID of the static site. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module staticSites './Microsoft.Web/staticSites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StaticSites' + params: { + name: '<>-az-wss-min-001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-wss-min-001" + } + } +} +``` + +
+

+ +

Example 2: Parameters

+ +
+ +via Bicep module + +```bicep +module staticSites './Microsoft.Web/staticSites/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-StaticSites' + params: { + // Required parameters + name: '<>-az-wss-x-001' + // Non-required parameters + allowConfigFileUpdates: true + enterpriseGradeCdnStatus: 'Disabled' + lock: 'CanNotDelete' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurestaticapps.net' + ] + } + service: 'staticSites' + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + } + ] + roleAssignments: [ + { + principalIds: [ + '<>' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'Standard' + stagingEnvironmentPolicy: 'Enabled' + systemAssignedIdentity: true + userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} + } + } +} +``` + +
+

+ +

+ +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": "<>-az-wss-x-001" + }, + // Non-required parameters + "allowConfigFileUpdates": { + "value": true + }, + "enterpriseGradeCdnStatus": { + "value": "Disabled" + }, + "lock": { + "value": "CanNotDelete" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azurestaticapps.net" + ] + }, + "service": "staticSites", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "<>" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "Standard" + }, + "stagingEnvironmentPolicy": { + "value": "Enabled" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {} + } + } + } +} +``` + +
+

diff --git a/modules/Microsoft.Web/staticSites/version.json b/modules/Microsoft.Web/staticSites/version.json new file mode 100644 index 0000000..41f66cc --- /dev/null +++ b/modules/Microsoft.Web/staticSites/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000..3fc8847 --- /dev/null +++ b/modules/README.md @@ -0,0 +1,113 @@ +In this section you can find useful information regarding the Modules that are contained in this repository. + +## Available Resource Modules + +| Name | Provider namespace | Resource Type | +| - | - | - | +| [Azure Active Directory Domain Services](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AAD/DomainServices) | `MS.AAD` | [DomainServices](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AAD/DomainServices) | +| [Analysis Services Servers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AnalysisServices/servers) | `MS.AnalysisServices` | [servers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AnalysisServices/servers) | +| [API Management Services](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ApiManagement/service) | `MS.ApiManagement` | [service](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ApiManagement/service) | +| [App Configuration](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AppConfiguration/configurationStores) | `MS.AppConfiguration` | [configurationStores](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.AppConfiguration/configurationStores) | +| [Authorization Locks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/locks) | `MS.Authorization` | [locks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/locks) | +| [Policy Assignments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyAssignments) | | [policyAssignments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyAssignments) | +| [Policy Definitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyDefinitions) | | [policyDefinitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyDefinitions) | +| [Policy Exemptions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyExemptions) | | [policyExemptions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policyExemptions) | +| [Policy Set Definitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policySetDefinitions) | | [policySetDefinitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/policySetDefinitions) | +| [Role Assignments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/roleAssignments) | | [roleAssignments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/roleAssignments) | +| [Role Definitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/roleDefinitions) | | [roleDefinitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Authorization/roleDefinitions) | +| [Automation Accounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Automation/automationAccounts) | `MS.Automation` | [automationAccounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Automation/automationAccounts) | +| [Batch Accounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Batch/batchAccounts) | `MS.Batch` | [batchAccounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Batch/batchAccounts) | +| [Cache Redis](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Cache/redis) | `MS.Cache` | [redis](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Cache/redis) | +| [Cognitive Services](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.CognitiveServices/accounts) | `MS.CognitiveServices` | [accounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.CognitiveServices/accounts) | +| [Availability Sets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/availabilitySets) | `MS.Compute` | [availabilitySets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/availabilitySets) | +| [Disk Encryption Sets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/diskEncryptionSets) | | [diskEncryptionSets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/diskEncryptionSets) | +| [Compute Disks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/disks) | | [disks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/disks) | +| [Azure Compute Galleries](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/galleries) | | [galleries](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/galleries) | +| [Images](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/images) | | [images](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/images) | +| [Proximity Placement Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/proximityPlacementGroups) | | [proximityPlacementGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/proximityPlacementGroups) | +| [Virtual Machines](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/virtualMachines) | | [virtualMachines](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/virtualMachines) | +| [Virtual Machine Scale Sets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/virtualMachineScaleSets) | | [virtualMachineScaleSets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Compute/virtualMachineScaleSets) | +| [Budgets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Consumption/budgets) | `MS.Consumption` | [budgets](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Consumption/budgets) | +| [Container Instances](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerInstance/containerGroups) | `MS.ContainerInstance` | [containerGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerInstance/containerGroups) | +| [Container Registries](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerRegistry/registries) | `MS.ContainerRegistry` | [registries](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerRegistry/registries) | +| [Azure Kubernetes Services](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerService/managedClusters) | `MS.ContainerService` | [managedClusters](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ContainerService/managedClusters) | +| [Azure Databricks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Databricks/workspaces) | `MS.Databricks` | [workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Databricks/workspaces) | +| [Data Factories](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DataFactory/factories) | `MS.DataFactory` | [factories](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DataFactory/factories) | +| [DataProtection BackupVaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DataProtection/backupVaults) | `MS.DataProtection` | [backupVaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DataProtection/backupVaults) | +| [DBforPostgreSQL FlexibleServers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DBforPostgreSQL/flexibleServers) | `MS.DBforPostgreSQL` | [flexibleServers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DBforPostgreSQL/flexibleServers) | +| [AVD Application Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/applicationgroups) | `MS.DesktopVirtualization` | [applicationgroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/applicationgroups) | +| [AVD Host Pools](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/hostpools) | | [hostpools](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/hostpools) | +| [AVD Scaling Plans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/scalingplans) | | [scalingplans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/scalingplans) | +| [AVD Workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/workspaces) | | [workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DesktopVirtualization/workspaces) | +| [DocumentDB Database Accounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DocumentDB/databaseAccounts) | `MS.DocumentDB` | [databaseAccounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.DocumentDB/databaseAccounts) | +| [Event Grid System Topics](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventGrid/systemTopics) | `MS.EventGrid` | [systemTopics](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventGrid/systemTopics) | +| [Event Grid Topics](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventGrid/topics) | | [topics](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventGrid/topics) | +| [Event Hub Namespaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventHub/namespaces) | `MS.EventHub` | [namespaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.EventHub/namespaces) | +| [Azure Health Bots](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.HealthBot/healthBots) | `MS.HealthBot` | [healthBots](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.HealthBot/healthBots) | +| [Action Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/actionGroups) | `MS.Insights` | [actionGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/actionGroups) | +| [Activity Log Alerts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/activityLogAlerts) | | [activityLogAlerts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/activityLogAlerts) | +| [Application Insights](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/components) | | [components](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/components) | +| [Activity Logs](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/diagnosticSettings) | | [diagnosticSettings](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/diagnosticSettings) | +| [Metric Alerts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/metricAlerts) | | [metricAlerts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/metricAlerts) | +| [Azure Monitor Private Link Scopes](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/privateLinkScopes) | | [privateLinkScopes](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/privateLinkScopes) | +| [Scheduled Query Rules](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/scheduledQueryRules) | | [scheduledQueryRules](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Insights/scheduledQueryRules) | +| [Key Vaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KeyVault/vaults) | `MS.KeyVault` | [vaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KeyVault/vaults) | +| [Kubernetes Configuration Extensions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KubernetesConfiguration/extensions) | `MS.KubernetesConfiguration` | [extensions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KubernetesConfiguration/extensions) | +| [Kubernetes Configuration Flux Configurations](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KubernetesConfiguration/fluxConfigurations) | | [fluxConfigurations](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.KubernetesConfiguration/fluxConfigurations) | +| [Logic Apps](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Logic/workflows) | `MS.Logic` | [workflows](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Logic/workflows) | +| [Machine Learning Workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.MachineLearningServices/workspaces) | `MS.achineLearningServices` | [workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.MachineLearningServices/workspaces) | +| [User Assigned Identities](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ManagedIdentity/userAssignedIdentities) | `MS.anagedIdentity` | [userAssignedIdentities](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ManagedIdentity/userAssignedIdentities) | +| [Registration Definitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ManagedServices/registrationDefinitions) | `MS.anagedServices` | [registrationDefinitions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ManagedServices/registrationDefinitions) | +| [Management Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Management/managementGroups) | `MS.anagement` | [managementGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Management/managementGroups) | +| [Azure NetApp Files](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.NetApp/netAppAccounts) | `MS.NetApp` | [netAppAccounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.NetApp/netAppAccounts) | +| [Network Application Gateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/applicationGateways) | `MS.Network` | [applicationGateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/applicationGateways) | +| [Application Security Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/applicationSecurityGroups) | | [applicationSecurityGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/applicationSecurityGroups) | +| [Azure Firewalls](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/azureFirewalls) | | [azureFirewalls](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/azureFirewalls) | +| [Bastion Hosts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/bastionHosts) | | [bastionHosts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/bastionHosts) | +| [Virtual Network Gateway Connections](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/connections) | | [connections](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/connections) | +| [DDoS Protection Plans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/ddosProtectionPlans) | | [ddosProtectionPlans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/ddosProtectionPlans) | +| [ExpressRoute Circuits](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/expressRouteCircuits) | | [expressRouteCircuits](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/expressRouteCircuits) | +| [Firewall Policies](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/firewallPolicies) | | [firewallPolicies](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/firewallPolicies) | +| [Front Doors](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/frontDoors) | | [frontDoors](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/frontDoors) | +| [IP Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/ipGroups) | | [ipGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/ipGroups) | +| [Load Balancers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/loadBalancers) | | [loadBalancers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/loadBalancers) | +| [Local Network Gateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/localNetworkGateways) | | [localNetworkGateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/localNetworkGateways) | +| [NAT Gateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/natGateways) | | [natGateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/natGateways) | +| [Network Interface](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkInterfaces) | | [networkInterfaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkInterfaces) | +| [Network Security Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkSecurityGroups) | | [networkSecurityGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkSecurityGroups) | +| [Network Watchers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkWatchers) | | [networkWatchers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/networkWatchers) | +| [Private DNS Zones](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateDnsZones) | | [privateDnsZones](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateDnsZones) | +| [Private Endpoints](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateEndpoints) | | [privateEndpoints](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateEndpoints) | +| [Network PrivateLinkServices](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateLinkServices) | | [privateLinkServices](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/privateLinkServices) | +| [Public IP Addresses](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/publicIPAddresses) | | [publicIPAddresses](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/publicIPAddresses) | +| [Public IP Prefixes](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/publicIPPrefixes) | | [publicIPPrefixes](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/publicIPPrefixes) | +| [Route Tables](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/routeTables) | | [routeTables](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/routeTables) | +| [Traffic Manager Profiles](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/trafficmanagerprofiles) | | [trafficmanagerprofiles](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/trafficmanagerprofiles) | +| [Virtual Hubs](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualHubs) | | [virtualHubs](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualHubs) | +| [Virtual Network Gateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualNetworkGateways) | | [virtualNetworkGateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualNetworkGateways) | +| [Virtual Networks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualNetworks) | | [virtualNetworks](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualNetworks) | +| [Virtual WANs](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualWans) | | [virtualWans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/virtualWans) | +| [VPN Gateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/vpnGateways) | | [vpnGateways](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/vpnGateways) | +| [VPN Sites](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/vpnSites) | | [vpnSites](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Network/vpnSites) | +| [Log Analytics Workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.OperationalInsights/workspaces) | `MS.OperationalInsights` | [workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.OperationalInsights/workspaces) | +| [OperationsManagement Solutions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.OperationsManagement/solutions) | `MS.OperationsManagement` | [solutions](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.OperationsManagement/solutions) | +| [PowerBIDedicated Capacities](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.PowerBIDedicated/capacities) | `MS.PowerBIDedicated` | [capacities](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.PowerBIDedicated/capacities) | +| [Recovery Services Vaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.RecoveryServices/vaults) | `MS.RecoveryServices` | [vaults](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.RecoveryServices/vaults) | +| [Deployment Scripts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/deploymentScripts) | `MS.Resources` | [deploymentScripts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/deploymentScripts) | +| [Resource Groups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/resourceGroups) | | [resourceGroups](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/resourceGroups) | +| [Resources Tags](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/tags) | | [tags](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Resources/tags) | +| [Azure Security Center](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Security/azureSecurityCenter) | `MS.Security` | [azureSecurityCenter](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Security/azureSecurityCenter) | +| [Service Bus Namespaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ServiceBus/namespaces) | `MS.ServiceBus` | [namespaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ServiceBus/namespaces) | +| [Service Fabric Clusters](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ServiceFabric/clusters) | `MS.ServiceFabric` | [clusters](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.ServiceFabric/clusters) | +| [Web PubSub Services](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.SignalRService/webPubSub) | `MS.SignalRService` | [webPubSub](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.SignalRService/webPubSub) | +| [SQL Managed Instances](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Sql/managedInstances) | `MS.Sql` | [managedInstances](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Sql/managedInstances) | +| [SQL Servers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Sql/servers) | | [servers](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Sql/servers) | +| [Storage Accounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Storage/storageAccounts) | `MS.Storage` | [storageAccounts](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Storage/storageAccounts) | +| [Azure Synapse Analytics](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Synapse/privateLinkHubs) | `MS.Synapse` | [privateLinkHubs](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Synapse/privateLinkHubs) | +| [Synapse Workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Synapse/workspaces) | | [workspaces](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Synapse/workspaces) | +| [Image Templates](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.VirtualMachineImages/imageTemplates) | `MS.VirtualMachineImages` | [imageTemplates](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.VirtualMachineImages/imageTemplates) | +| [API Connections](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/connections) | `MS.Web` | [connections](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/connections) | +| [App Service Environments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/hostingEnvironments) | | [hostingEnvironments](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/hostingEnvironments) | +| [App Service Plans](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/serverfarms) | | [serverfarms](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/serverfarms) | +| [Web/Function Apps](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/sites) | | [sites](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/sites) | +| [Static Web Apps](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/staticSites) | | [staticSites](https://github.com/Azure/ResourceModules/tree/main/modules/Microsoft.Web/staticSites) |